2009-10-15 3 views
4

Vue d'ensemble:l'aide d'une « variation » sur le modèle de l'Etat - C++

Je suis en train d'améliorer la conception d'un programme que j'utilise modèle d'état pour. Je vais poster une brève description du problème, une image du diagramme de classe/description de la conception actuelle, suivie par un code d'en-tête pour les classes pertinentes.

Problème:

J'utilise une variante du modèle d'État pour un programme. Dans cette variante, j'ai un «contrôleur» qui utilise deux classes abstraites, «état» et «événement», dont plusieurs types concrets sont étendus des deux. Ces deux classes abstraites sont utilisées pour effectuer des réponses aux «événements» qui varient en fonction du type d'événement et de l'état actuel. Chaque état a une fonction 'handler' qui est surchargée pour prendre chaque type d'événement concret. Le 'controller' contient une file d'attente de type 'event' (la classe abstraite), qui contient la liste des 'événements' (classes concrètes) qui se sont produits. Le contrôleur «traite» chaque événement un à la fois, en le récupérant de la file d'attente et en le passant au gestionnaire de l'état pour ce type particulier d'événement.

Le problème est que, pour obtenir le bon type de passer l'événement au gestionnaire approprié de l'état, je dois dégradé l'événement au type de béton correct. Actuellement, j'accomplis cela en ajoutant une méthode à la classe 'state' (getType()) qui est implémentée par chaque type d'événement concret, et retourne un entier qui représente ce type d'événement. Cependant, cette pratique est très «peu élégante» et se traduit par l'utilisation d'énumérations pour piloter les blocs «switch» et «downcasting» - qui ne sont pas de très bonnes pratiques de conception.

Comment est-ce que je peux changer cette conception pour rendre le passage des événements aux états plus élégants?

Diagramme de classes

Class diagram

tête 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); 
} 

Répondre

4

Ajouter une méthode abstraite:

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

à l'événement.

Et puis la mise en œuvre est la suivante dans chaque sous-classe de l'État:

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

puis dans la boucle simplement appeler:

nextState = nextEvent->transition(currentState); 

Edit:

Si vous faites événement une classe modèle basé sur un modèle sur sa sous-classe, vous pouvez implémenter une transition dans la classe d'événement elle-même:

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

Hey, J'essaie actuellement vos suggestions ... Si j'ajoute 'transition (state * from)' à la classe 'event', cela ne se traduira-t-il pas par 'circular-dependancy', où les classes 'event' et 'state' se réfèrent mutuellement? – simplyletgo

+1

Oui, ils se référeront tous les deux, mais c'est ok. Vous devez transférer l'état de classe avant l'événement de classe, c'est-à-dire l'état de la classe ; // forward déclaration class event {...} – atomice

+0

aha, je vois! Je n'avais même pas réalisé que vous pouviez faire ça avec des cours en C++, c'est plutôt cool. J'aurais pensé que cela aurait causé une sorte de 'problème de redéfinition' où le compilateur voit la définition de 'l'état de la classe' dans 'event.h' et 'state.h', et dit 'hell no'. – simplyletgo

Questions connexes