4

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

Class diagram

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* nextState;
    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 );
}
jaco0646
  • 15,303
  • 7
  • 59
  • 83
simplyletgo
  • 151
  • 5

2 Answers2

4

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));
    }
}
atomice
  • 3,062
  • 17
  • 23
  • Hey, I'm currently trying out your suggestions... If I add 'transition(state* from)' to class 'event', won't this result in 'circular-dependancy', where class 'event' and 'state' both reference each other? – simplyletgo Oct 15 '09 at 14:16
  • 1
    Yes they will both reference each other but that is ok. You need to forward declare class state before class event, i.e. class state; // forward declaration class event { ... } – atomice Oct 15 '09 at 14:46
  • aha, I see! I didn't even realize you could do that with classes in C++, that's pretty cool. I would have thought that would have caused some sort of 'redefinition problem' where the compiler sees the definition of 'class state' in both 'event.h' and 'state.h', and says 'hell no'. – simplyletgo Oct 15 '09 at 15:03
  • Of course, the templated version would still inherit from a 'EventInterface' class as manipulating the list of events require a common base class. – Matthieu M. Oct 20 '09 at 13:55
  • The 'event' class is the base class. – atomice Oct 20 '09 at 19:06
0

Wouldn't Double Dispatch help?

Xavier Nodet
  • 5,033
  • 2
  • 37
  • 48