views:

226

answers:

2

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 note - unfortunately due to my 'new user status' i stackoverflow won't let me embed an image using the appropriate tag, so I'm going to just have to give you the URL link.

http://picasaweb.google.com/lh/photo/W4Y5Cg1HKCa75YlyusJjJA?feat=directlink

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 );
}
A: 

Wouldn't Double Dispatch help?

Xavier Nodet
+3  A: 

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
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
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 declarationclass event { ... }
atomice
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
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.
The 'event' class is the base class.
atomice