2010-08-15 12 views
9

Ich bin dabei, eine hierarchische Zustandsmaschine in C# mithilfe des Zustandsmusters zu implementieren. Als Anleitung verwende ich this Beispiel. Das Beispiel liefert jedoch keine Antwort auf hierarchische Zustände. Leider kann ich nicht anderswo gute Beispiele finden. Mein erster Gedanke ist, geschachtelte Klassen für die hierarchischen Zustände zu erstellen. Aber gilt dies als Best Practice oder gibt es bessere Lösungen?Was ist die beste Vorgehensweise für eine hierarchische Zustandsmaschine, die das Zustandsmuster verwendet?

Grüßt!

UPDATE:

Ich habe den ganzen Nachmittag gesessen zu versuchen, den Zustand Muster zu implementieren, wie oben beschrieben. Der HSM basiert auf einem sehr einfachen Mediaplayer:

alt text http://www.freeimagehosting.net/uploads/e8d2d6486a.jpg

Ich dachte, ich es getan habe, aber eine Sache, die ich nicht verstehe. Zunächst wird der Code, den ich geschrieben habe (sorry, es ist ziemlich al lot):

public class MediaPlayer 
{ 
    public MediaPlayerStates state; 

    public MediaPlayer(MediaPlayerStates state) 
    { 
     this.state = state; 
    } 

    public void OnButtonPressed() 
    { 
     state.OnButtonPressed(this); 
    } 

    public void DeviceBooted() 
    { 
     state. ????? 
    } 

    //Other Functions 
} 

//The 3 initial states (Start, On, End) know only 2 events. 
public abstract class MediaPlayerStates 
{ 
    public abstract void OnButtonPressed(MediaPlayer player); 
    public abstract void OffButtonPressed(MediaPlayer player); 
} 

//The very beginpoint of the state machine 
public class Start : MediaPlayerStates 
{ 
    //When hitting the onbutton, the state changes to the OnState state 
    public override void OnButtonPressed(MediaPlayer player) 
    { 
     player.state = new OnState(player); 
    } 

    //No need to implement this one 
    public override void OffButtonPressed(MediaPlayer player) 
    { 
     throw new NotImplementedException(); 
    } 
} 

//OnState implements the 2 events from the MediaPlayerStates abstract class. 
public class OnState : MediaPlayerStates 
{ 
    //When entered the OnState state, a new entrypoint is creaeted: the Start state 
    public OnState(MediaPlayer player) 
    { 
     player.state = new OnStartState(); 
    } 

    //The OnState doesn't have a OnButtonPressed event so it doesn't need to be implemented 
    public override void OnButtonPressed(MediaPlayer player) 
    { 
     throw new NotImplementedException(); 
    } 

    //When hitting the offbutton in the OnState, the new state is End 
    public override void OffButtonPressed(MediaPlayer player) 
    { 
     player.state = new End(); 
    } 

    //The OnState itself containts 3 events, therefore these need to be implemented by every state whitin the OnState state 
    public abstract class SubStates : MediaPlayerStates 
    { 
     public abstract void DeviceBooted(MediaPlayer player); 
     public abstract void PlayButtonPressed(MediaPlayer player); 
     public abstract void StopButtonPressed(MediaPlayer player); 
    } 

    //The OnStartState is the pseudoState where the On state starts 
    public class OnStartState : SubStates 
    { 
     //When booted, the state of the player changes to the ShowMediaFileState state 
     public override void DeviceBooted(MediaPlayer player) 
     { 
      player.state = new ShowMediaFileState(); 
     } 

     //The events below don't need to be implemented since they don't exist. 
     public override void PlayButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     public override void StopButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     public override void OnButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     public override void OffButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public class ShowMediaFileState : SubStates 
    { 
     //This event doesn't exists for this state 
     public override void DeviceBooted(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     //When hitting the play button in this state, play the mediafile 
     public override void PlayButtonPressed(MediaPlayer player) 
     { 
      player.state = new PlayMediaFileState(); 
     } 

     //These events also don't exist for this state 
     public override void StopButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     public override void OnButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     public override void OffButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 
    } 

    public class PlayMediaFileState : SubStates 
    { 
     //This event doesn't exist for this state 
     public override void DeviceBooted(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     //This event doesn't exist for this state 
     public override void PlayButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     //While playing a file and hitting the stopbutton, the state changes to the ShowMediaFileState state 
     public override void StopButtonPressed(MediaPlayer player) 
     { 
      player.state = new ShowMediaFileState(); 
     } 

     //This event doesn't exist for this state 
     public override void OnButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 

     //This event doesn't exist for this state 
     public override void OffButtonPressed(MediaPlayer player) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
} 

//The endstate doesn't need any implementation since there cannot occur a event while being off 
public class End : MediaPlayerStates 
{ 
    public override void OnButtonPressed(MediaPlayer player) 
    { 
     throw new NotImplementedException(); 
    } 

    public override void OffButtonPressed(MediaPlayer player) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Wenn in der Mediaplayer-Klasse, die Ereignisse zu definieren, kann ich keine anderen Funktionen aufrufen dann

  • OnButtonPressed
  • OffButtonPressed

Also, ich frage mich, ist meine Implementierung gut? Was ist falsch? Ich habe auch versucht, den Vorschlag der Verwendung des zusammengesetzten Musters zu betrachten, aber ich verstehe nicht, wie es mit dem Zustandsmuster verwendet werden sollte. Hoffe, dass jeder helfen kann!

+0

Haben Sie darüber nachgedacht IEnumerable und ergeben? Sie bieten einfache Maschinenmechanik direkt in der Sprache. z.B. http://www.yoda.arachsys.com/csharp/csharp2/iterators.html (eines von vielen Beispielen im Netz) – Will

+0

Soweit ich Ihren Vorschlag sehen kann, ist keine gute Idee. Da ich neu im Konzept bin, habe ich danach gesucht und folgendes gefunden: http://stackoverflow.com/questions/1194853/implementing-a-state-machine-using-the-yield-keyword Trotzdem schätze ich Ihre Eingabe :) – user341877

Antwort

3

Ich denke, Sie wollen auch Composite; damit können Sie Zustandsautomaten miteinander verbinden.

1

Bevor Sie mit der Implementierung Ihres eigenen FSM-Frameworks beginnen, werfen Sie einen Blick auf SMC - den State Machine Compiler.

SMC nimmt eine Textdefinition einer Zustandsmaschine und generiert den Code, um es zu implementieren. Es hat Backends für eine Vielzahl von Sprachen einschließlich C#. Es kann auch Punktdateien ausgeben, um ein Diagramm der FSM zu erzeugen.

SMC kann mit den Push- und Pop-Übergängen etwas Ähnliches wie hierarchische Statusmaschinen erstellen - im Wesentlichen push überträgt die Steuerung an eine neue Statusmaschine und pop gibt die Steuerung an die ursprüngliche Statusmaschine zurück.

+0

Wie toll ein Framework aussehen könnte, das eine FSM generieren kann, möchte ich lieber versuchen, meine eigene FSM zu erstellen. Abgesehen davon möchte ich nicht viele Framework-Dateien in meinem Projekt haben. Ich möchte nur eine einfache und saubere FSM erstellen. – user341877

2

Um ein HSM mit dem Zustandsmuster zu erstellen, muss jeder Zustand mit Unterzuständen selbst eine Zustandsmaschine sein. Auf diese Weise hat die obere Ebene keine Kenntnis über die Unterzustände (weniger Nebenwirkungen) und der Staat kann seine Unterzustände besser verwalten (kann einen Standardzustand haben, kann sich an den letzten Zustand erinnern, in dem er sich befand usw.).

BTW wirft eine Ausnahme, wenn Sie nichts Nützliches mit einer Aktion tun können, ist falsch. Sie sollten es einfach ignorieren. Ausnahmen werden nur in Ausnahmefällen ausgelöst. Wenn Sie eine falsche Schaltfläche drücken, wird das Benutzerverhalten erwartet.

+0

+1: "... das Drücken eines falschen Knopfes ist das erwartete Benutzerverhalten." Das ist ganz normal. Es gibt sehr wenige Ausnahmefälle - ich wünschte, Programmierer würden das verstehen! – quamrana

1

Damit es generisch funktioniert, müssen Sie die Hierarchie der Statusmaschine als Baumstruktur behandeln. Übergänge zwischen Knoten können einen Least-Common-Ancestor (LCA) -Algorithmus für Bäume verwenden und dann vom Knoten unter dem gemeinsamen Vorfahren auf dem Ursprung des Quellknotens (Kaskadierung des Exits auf alle untergeordneten Knoten) austreten und dann jeden Knoten auf dem Zielknoten-Ursprung eingeben Wenn der Zielknoten untergeordnete Knoten hat, müssen Sie diese so eingeben, als würden Sie einen anderen zusammengesetzten Zustand eingeben.

Dies ist die Methode, die in der UML Superstructure Specification erwähnt wird.

Werfen Sie einen Blick auf den Quellcode in https://github.com/steelbreeze/state.cs, da dies die obige Methode implementiert.

Um ein funktionierendes Beispiel zu sehen, wie das Projekt vor Ort für die Schwester JavaScript-Version siehe hier: http://www.steelbreeze.net/state.js/