开发者

Using a 'variation' on State Pattern - C++

开发者 https://www.devze.com 2022-12-08 20:04 出处:网络
Overview: I am trying to improve the design of a program that I am using state pattern for.I will post a brief description of the problem, an image of the class diagram/description of the current de

Overview:

I am trying to improve the design of a program that I am using state pattern for. I will post a brief description of the problem, an image of the class diagram/description of the current design, followed by header code for the relevant classes.

Problem:

I'm using a variation of the State Pattern for a program. In this variation, I have a 'controller' that utilizes two abstract classes, 'state' and 'event', which several concrete types are extended from both. These two abstract classes are used to carry out responses to 'events' that vary based upon the type of event, and the current state. Each state has a 'handler' function that is overloaded to take each concrete event type.

The 'controller' contains a queue of type 'event' (the abstract class), which contains the list of of 'events' (concrete classes) that have occurred. The controller 'processes' each event one at a time, by retrieving it from the queue, and passing it to the state's handler for that particular type of event.

The problem is that in order to get the correct type to pass the event to the state's appropriate handler, I have to downcast the event to the correct concrete type. Currently, I accomplish this by adding a method to class 'state' (getType()) which is implemented by each concrete event type, and returns an integer that represents that event type. However, this practice is very 'un-elegant' and results in using enums to drive 'switch' blocks and 'downcasting' - which are not very good design practices.

How can I change this design to make the passing of events to states more elegant?

Class Diagram

Using a 'variation' on State Pattern - C++

Header Code

/*****
 * CONTROLLER (driver) CLASS
 */
 queue<event> events; //gets populated by other threads that hold reference to it
 state* currentState;
 vector<state*> allStates;
 allStates.push_back( new state_1(&allStates) ); // passes reference to 'allStates' to each state
 allStates.push_back( new state_2(&allStates) ); //   so that it may return the 'next state'
 ...

while( true ){
    event nextEvent;
    state* nextSta开发者_如何学Pythonte;
    if( events.size() > 0 ){
        nextEvent = events.front(); //Get next Event
        events.pop(); //remove from queue
        switch( nextEvent.getType() ){ //determine 'concrete type' based on 'getType method'
            case 1: 
                //Downcast to concrete state type, and let state handle event
                nextState = currentState->handle( *dynamic_cast<event_type_1*>(&nextEvent) ); 
                break;
            case 2:
                state* nextState = currentState->handle( *dynamic_cast<event_type_1*>(&nextEvent) );
                break;
            ...
        }
        //Transition to next state
        currentState = nextState;
    else
        Sleep(5); //
}

/*****
 * EVENT CLASSES
 */
class event{
    public:
        virtual int getType();
}

class event_type_1 : public event{
    public:
        int getType(){ return 1; };
        int specializedFunc1();
        double specializedFunc2();
}

class event_type_2 : public event{
    public:
        int getType(){ return 2; };
        std::string specializedFunc3();
}

/*****
 * STATE CLASSES
 */
class state{
    protected:
        vector<state*>* m_states;
    public:
        state( vector<state*>* p_states ){ m_states = p_states; };
        virtual state* handle( event_type_1 );
        virtual state* handle( event_type_2 );    
}

class state_1 : public state{
    public:
        state* handle( event_type_1 );
        state* handle( event_type_2 );
}

class state_2 : public state{
    public:
        state* handle( event_type_1 );
        state* handle( event_type_2 );
}


Add an abstract method:

virtual state *transition(state *from) = 0;

to event.

And then implement is as follows in each state subclass:

state *transition(state *from)
{
    from->handle(this);
}

then in your loop simply call:

nextState = nextEvent->transition(currentState);

Edit:

If you make event a template class templated on its subclass you can implement transition in the event class itself:

template <class subclass> class event
{
    state *transition(state *from)
    {
        from->handle(dynamic_cast<subclass *>(this));
    }
}


Wouldn't Double Dispatch help?

0

精彩评论

暂无评论...
验证码 换一张
取 消