2013-12-21 2 views
9

Ich habe oft die Notwendigkeit, ein Objekt zu implementieren, das in der Lage ist, sein Verhalten als Reaktion auf einen Benutzerbefehl zu wechseln. Dies könnte zum Beispiel der Fall sein, wenn ein Gerät der Klasse repräsentativ mit einem PC verbunden ist und vom Benutzer über eine GUI gesteuert wird. Allgemeiner muss das Gerät selbständig sein und eine eigene Ablaufplanung haben. enter image description here Da ich dieses Verhalten aus der spezifischen Geräteklasse "extrahieren" möchte, um die Code-Wiederverwendung zu verbessern, schlage ich hier eine Templates endliche Zustandsmaschine Klasse mit Qt. Ich habe auch eine Beispielverwendung in Klasse A gemeldet. Was denkst du (erfahrenere Programmierer als ich) darüber? Ist es der "richtige" Weg, eine solche Klasse zu entwerfen? Gibt es Leistungsprobleme?Eine "verallgemeinerte" endliche Automatenimplementierung

template < class Base, 
      typename T, 
      class ThreadPolicy> 
class FSM 
{ 
public: 
    typedef bool (Base::*my_func)(); 
    struct SState { 
     SState(){} 
     SState(const T& id_arg, 
       const T& next_arg, 
       const T& error_arg, 
       const QList<T>& branches_arg, 
       const my_func& op_arg) : 
      id(id_arg), 
      next(next_arg), 
      error(error_arg), 
      branches(branches_arg), 
      op(op_arg) 
     {} 
     T id;  // state ID 
     T next; // next state 
     T error; // in case of error 
     QList<T> branches; // allowed state switching from current 
     my_func op; // operation associated with current state 
    }; 
    typedef QMap<T ,SState> SMap; 
    bool switchState(const T& ns){ 
     return _checkAllowed(ns); 
    } 
    bool addState(const T& id, const SState& s){ 
     return _register(id, s); 
    } 
protected: 

    void _loop(Base* ptr){ 
     if ((ptr->*m_states[m_state].op)()) { 
      ThreadPolicy::Lock(); 
      if(m_externalSwitch){ 
       m_externalSwitch = false; 
       ThreadPolicy::Unlock(); 
       return; 
      } 
      m_state = m_states[m_state].next; 
      ThreadPolicy::Unlock(); 
     } else { 
      ThreadPolicy::Lock(); 
      if(m_externalSwitch){ 
       m_externalSwitch = false; 
       ThreadPolicy::Unlock(); 
       return; 
      } 
      m_state = m_states[m_state].error; 
      ThreadPolicy::Unlock(); 
     } 
    } 
    bool _checkAllowed(const T& cmd){ 
     if (!m_states[m_state].branches.contains(cmd)) { return false;} 
     ThreadPolicy::Lock(); 
     m_state = cmd; 
     m_externalSwitch = true; 
     ThreadPolicy::Unlock(); 
     return true; 
    } 

    bool _register(const SState& s){ 
     if(m_states.find(s.id) != m_states.end()) { return false; } // state with same ID already exist 
     m_states[s.id] = s; // add the new state to the map 
     return true; 
    } 
    SMap m_states; // map states to Baseclass methods 
    T m_state; // holds my current state 
    bool m_externalSwitch; // check if user request a state switch 
}; 

class A : 
     public QObject, 
     public FSM< A, QString, MultiThreaded > 
{ 
    Q_OBJECT 
    A(){ 
//  SState startState; myState.branches << "start" << "stop"; 
     _register(SState("start", 
         "start", 
         "stop",QStringList(("start","stop")), 
         &A::_doStart)); 
     _register(SState("stop", 
         "stop", 
         "stop",QStringList(("stop","start")), 
         &A::_doStop)); 
    } 

private slots: 
    void run(){ 
     for(;;){ 
      _loop(this); 
      QCoreApplication::processEvents(); 
     } 
    } 
private: 
    bool _doStart(){ return true;} 
    bool _doStop(){ return true;} 

}; 
+0

BTW die Frage, die Sie würde besser passen für SO-Code-Review-Website sind gefragt, wenn Sie wollen einfach nur wissen, ob Ihr Code gut ist oder nicht: http: // Codereview .stackexchange.com –

Antwort

8

A. Was Sie (mehr erfahrene Programmierer als ich :) denken darüber? Ist es der "richtige" Weg, eine solche Klasse zu entwerfen? Gibt es Leistungsprobleme?

OK! Ich habe mir deinen Entwurf grob angesehen und es fühlt sich für ein allgemeines FSM-Framework nicht wirklich gut an. Das ist zu eng, um in einem erweiterten Kontext verwendbar zu sein. Einige Kritikpunkte:.

  1. Sie sind abhängig von Qt :(; zumindest sollten Sie C++ STL-Komponenten für Ihre Implementierungsdetails verwenden
  2. Ihre Staaten sein sollte (Fach-) Klassen auf ihre eigenen, daß das Gerät das Verhalten direkt. die FSM-Klasse selbst sollte unabhängig seine (vor allem nicht umsetzen) aus ihrem Verhalten.
  3. Sie nicht komplexen Zustandsdiagramme, einschließlich Unterzustand (Maschinen)/Composite-Staaten, gleichzeitig FSM unterstützen Pathes (Gabel, Junctions), aktive Zustände (wiederholte asynchrone Do-Operationen), ...

Im Allgemeinen würde ich empfehlen, die GoF State Pattern für FSM-Implementierung zu folgen. Für sehr einfache Zustandsdiagramme könnte ein switch(event)case <event>: changeState(newState) ausreichen. Aber Ereignisse als Methodeneinträge der FSM zu rendern und diese an die aktuelle Zustandsklasseninstanz zu delegieren, macht das ganze Konstrukt viel flexibler. Denken Sie an optionale Parameter, die mit einem bestimmten Ereignis einhergehen, und Sie müssen das Design Ihrer Zustandsmaschine für diese erweitern.

Im Allgemeinen ist Ihr Ansatz, eine CRTP für Ihre Zustandsmaschine zu verwenden, eine gute Idee, aber für das, was Sie demonstrierten, würde einfacher dynamischer Polymorphismus (mit virtuellen Elementfunktionen) auch gut funktionieren.

Über Leistungsprobleme, glaube nicht, dass Sie mit Ihrer aktuellen Umgebung schlagen werden, aber das hängt völlig davon ab, wo und in welchem ​​Kontext Sie bereitstellen möchten.

Ich möchte Ihnen empfehlen, einen Blick auf mein State Machine Class Template-Framework STTCL zu werfen, das verschiedene C++ vorlagenbasierte Aspekte von UML 2.0-konformen State-Rechnern nach dem bereits erwähnten GoF State Pattern bietet.

0

Wenn es noch relevant ist, habe ich einen endlichen Automaten in C++ implementiert, der das Objekt OP verwendet, es ist ziemlich einfach zu benutzen und wenn Sie sich die main.cpp ansehen, gibt es ein Beispiel.

Der Code ist hier und es ist jetzt als eine Bibliothek kompiliert.

Finite State Machine

Lassen Sie mich wissen, ob es was Sie wollen, ist!

Cheers,

Andrea