2010-04-24 15 views
7

Tengo un pequeño problema que implica el modelado de una máquina de estado.Diseñando una máquina de estado en C++

He logrado hacer un poco de ingeniería del conocimiento e "ingeniería inversa" un conjunto de reglas deterministas primitivas que determinan las transiciones estatales y estatales.

me gustaría saber cuáles son las mejores prácticas en relación con:

  • cómo probar rigurosamente mis estados y transiciones de estado para asegurarse de que el sistema no puede terminar en un estado indeterminado.

  • Cómo hacer cumplir los requisitos de transición de estados (por ejemplo, debería ser imposible ir directamente desde stateFoo a StateFooBar, es decir, para imbuir a cada estado con el 'conocimiento' acerca de los estados que puede pasar a.

Idealmente, me gustaría utilizar un diseño limpio, basado en patrones, con plantillas siempre que sea posible.

Necesito un lugar para empezar y agradecería cualquier apuntador (sin juego de palabras), que se envíe a mi manera.

+0

búsqueda de "validación formal máquina de estados" – bobah

Respuesta

7

Asegúrese de echar un vistazo a la Boost Statechart Library.

+0

también echar un vistazo a los HSH (http: // www. boost.org/doc/libs/1_55_0/libs/msm/doc/HTML/index.html), son muy diferentes. – bobah

0

Suena como una aplicación prístina para pruebas unitarias. Hay muchos marcos de pruebas unitarias por ahí. Me gusta el Boost one.

1

Las pruebas tienen poco que ver con patrones, plantillas, etc. Recomendaría un marco de prueba como CppUnit (parte de la familia xUnit) para capturar todos sus casos de prueba. El número dependerá de la complejidad de la máquina de estado, por supuesto.

Su pregunta acerca de forzar las transiciones de estado va al corazón del diseño de su clase para su máquina de estado. Yo diría que un estado tendrá una colección de estados secundarios a los que podría hacer la transición, junto con el evento que activará cada uno. Si el evento Foo no tiene un hijo FooBar, entonces no hay forma de hacer la transición.

Buscaría Google "máquinas de estados finitos orientadas a objetos" para comenzar a obtener algunas ideas de diseño.

Cuando estaba pensando en problemas como este, pensé que el patrón de diseño compuesto podría ser parte de él, porque un estado podría representar un FSM más complejo. Tendría una interfaz de estado, con SimpleState y CompositeState como implementaciones. Tendría que comenzar de nuevo y ver si todo podía funcionar.

3

Gosh, no es tan complicado como parece. El código de máquina de estado es muy simple y corto.

Almacena el estado en una variable, digamos myState.

Tu máquina de estado será una declaración de cambio, ramificándose en el valor de la variable myState para ejercitar el código para cada estado.

El código estará lleno de líneas de este tipo:

myState = newState; 

Para hacer cumplir los requisitos de transición de estado, es necesario agregar un poco de método llamado en su lugar, como este

void DoSafeStateTransition(int newState) 
{ 
// check myState -. newState is not forbidden 
// lots of ways to do this 
// perhaps nested switch statement 

switch(myState) { 

… 

case X: switch(newState) 
    case A: case B: case Z: HorribleError(newState); 
    break; 

... 

} 

// check that newState is not undetermined 

switch(newState) { 

// all the determined states 
case A: case B: case C … case Z: myState = newState; break; 
default: HorribleError(newState); 
} 
} 
void HorribleError(int newState) 
{ printf("Attempt to go from %d to %d - disallowed\n", 
     myState, newState); 
    exit(1); 
} 

Sugiero que esta lo suficientemente simple y corto como para que la inspección funcione mejor que las pruebas unitarias, ¡sin duda será mucho más rápido!

El punto, en mi mente, de las pruebas unitarias es que el código de prueba sea más sencillo que el código de prueba, para que pueda ser más fácilmente inspeccionada por la corrección, entonces se utiliza para probar el código complicado. A menudo es más fácil verificar el código de máquina de estado que el código de prueba de máquina de estado. No tiene mucho sentido informar un pase de prueba del 100% de la unidad, cuando tiene poca idea de si las pruebas de la unidad son correctas.

decirlo de otra manera: la codificación de una máquina de estados es fácil, diseñando el correcto es difícil. Las pruebas unitarias solo le informarán si ha codificado correctamente el diseño, no si el diseño fue correcto.

1

El uso de máquinas de estado es algo que viene de vez en cuando. Suelo hacer lo que se sugiere Ravenspoint y simplemente hacer una declaración de cambio. Pero eso solo funciona si los estados no son demasiado grandes. Esto suena como tu caso. Teniendo esto en cuenta, creo que lo mejor es comenzar con una buena arquitectura que permita algunas de las cosas que quiere hacer. Tomé la sugerencia de Duffymo y probé con Google. Este documento parecía interesante - Object-Oriented State Machines. Puede ser exagerado, pero creo que dará un marco que sería fácil de probar con algo como CppUnit.

Algunas otras buenas referencias de la búsqueda de Google

A Finite State Machine Framework

Object-Oriented Finite State Machines

0

Si usted está buscando para el clásico patrón de patrones de diseño GOF máquina de estado, y luego mirar wikipedia.

echar un vistazo a esta página (en el momento de la escritura) en el ejemplo de Java.

Tiene una clase StateContext, que puede ver en el ejemplo de uso, tiene clientes que conocen el método writeName. La implementación es: this.myState.writeName(this, name); lo que significa que reenvía la llamada al estado actual, pasando a sí mismo como el primer argumento.

Ahora mira interface State, tiene un método writeName que coincide con el uso anterior. Si observas StateA y StateB, vuelven a llamar al contexto estableciendo un nuevo estado.

Esa es la mayor parte del patrón Estado allí mismo. Lo único que se debe tener en cuenta es que la clase StateContext puede contener todos los datos involucrados en su funcionamiento, incluida una referencia (tiene que ser un puntero en C++) al estado actual. Todos los estados realizan en conjunto la totalidad de la conducta, pero no hay datos , en vez difiriendo los datos (además de los métodos de ayuda) en el contexto.

Cuando estoy desarrollando una máquina de estado (suelo usar TDD) no me molesto en probar las transiciones de estado, solo que el comportamiento final es el que deseo.

Cuestiones relacionadas