2010-08-15 16 views
9

Estoy a punto de implementar una máquina de estados jerárquica en C# utilizando el patrón de estado. Como guía, estoy usando el ejemplo this. Sin embargo, el ejemplo no proporciona una respuesta con respecto a los estados jerárquicos. Lamentablemente, no puedo encontrar buenos ejemplos en otros lugares. Mi primer pensamiento es crear clasificadas jerarquizadas para los estados jerárquicos. ¿Pero esto se considera la mejor práctica o hay mejores soluciones?¿Cuál es la mejor práctica para una máquina de estados jerárquica que usa el patrón de estado?

¡Saludos!

ACTUALIZACIÓN:

He estado sentado toda la tarde en tratar de implementar el patrón de estado como se describió anteriormente. El HSM se basa en un reproductor multimedia muy simple:

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

pensé que lo he hecho, pero una cosa que no entiendo. En primer lugar el código que he escrito (lo siento, es bastante muchos libre):

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(); 
    } 
} 

Al definir los eventos en la clase MediaPlayer, no puedo llamar a cualquier otra función a continuación

  • OnButtonPressed
  • OffButtonPressed

así que me pregunto, es mi aplicación bueno? ¿Qué está mal? También traté de ver la sugerencia de usar el patrón compuesto, pero no entiendo cómo se debe usar con el patrón de estado. Espero que alguien pueda ayudar!

+0

¿Ha considerado IEnumerable y el rendimiento? Proporcionan una mecánica de estado de máquina simple directa dentro del lenguaje. p.ej. http://www.yoda.arachsys.com/csharp/csharp2/iterators.html (uno de los muchos ejemplos en la red) – Will

+0

Por lo que veo, su sugerencia no es una buena idea. Como soy nuevo en el concepto lo busqué y encontré esto: http://stackoverflow.com/questions/1194853/implementing-a-state-machine-using-the-yield-keyword Sin embargo, agradezco su opinión. :) – user341877

Respuesta

3

Creo que también querrás Composite; eso le permitirá vincular máquinas de estado juntas.

1

Antes de comenzar a implementar su propia estructura de FSM, eche un vistazo a SMC - el compilador de máquina de estado.

SMC toma una definición textual de una máquina de estado y genera el código para implementarla. Tiene backends para una amplia gama de idiomas, incluido C#. También puede generar archivos de puntos para generar un diagrama del FSM.

SMC puede crear algo similar a las máquinas de estado jerárquico con las transiciones push y pop - esencialmente transfiere el control de transferencias a una nueva máquina de estado y pop devuelve el control a la máquina de estado original.

+0

Qué gran marco puede generar un FSM, prefiero intentar crear mi propio FSM. Además de eso, no quiero tener muchos archivos de framework en mi proyecto. Solo quiero crear un FSM simple y limpio. – user341877

2

Para hacer un HSM con el patrón de estado, cada estado con subestados tiene que ser una máquina de estados. De esta forma, el nivel superior no tiene conocimiento de los subestados (menos efectos secundarios) y el estado puede administrar mejor sus subestados (puede tener un estado predeterminado, puede recordar el último estado en el que estuvo, etc.).

BTW lanzando una excepción cuando no se puede hacer nada útil con una acción incorrecta. Deberías simplemente ignorarlo. Solo se lanzan excepciones en casos excepcionales, presionar un botón incorrecto es comportamiento esperado del usuario.

+0

+1: "... presionar un botón incorrecto es el comportamiento esperado del usuario". Esto es bastante normal. Hay muy pocos casos excepcionales. ¡Ojalá los programadores lo entendieran! – quamrana

1

Para que funcione de manera genérica, deberá tratar la jerarquía de la máquina de estado como una estructura de árbol; las transiciones entre nodos pueden usar un algoritmo de antecesor menos común (LCA) para árboles y luego salir del nodo debajo del ancestro común en el ascendente del nodo fuente (conectando en cascada la salida a cualquier nodo hijo), luego ingrese cada nodo en el ancestro del nodo objetivo del nodo debajo del ancestro común al nodo objetivo, finalmente, si el nodo objetivo tiene hijos, tendrá que ingresarlos como si estuviera ingresando en cualquier otro estado compuesto.

Este es el método que se menciona en la Especificación de superestructura UML.

Eche un vistazo al código fuente en https://github.com/steelbreeze/state.cs ya que implementa el método anterior.

Para ver un ejemplo de trabajo, echar un vistazo como el sitio del proyecto para la versión hermana JavaScript aquí: http://www.steelbreeze.net/state.js/

Cuestiones relacionadas