Implementación de máquinas de estado simples con enumeraciones de Java

1. Información general

En este tutorial, veremos State Machines y cómo se pueden implementar en Java usando Enums.

También explicaremos las ventajas de esta implementación en comparación con el uso de una interfaz y una clase concreta para cada estado.

2. Java Enums

Un Java Enum es un tipo especial de clase que define una lista de constantes. Esto permite una implementación segura de tipos y un código más legible .

Como ejemplo, supongamos que tenemos un sistema de software de recursos humanos que puede aprobar las solicitudes de licencia enviadas por los empleados. Esta solicitud es revisada por el líder del equipo, quien la eleva al gerente de departamento. El Gerente de Departamento es la persona responsable de aprobar la solicitud.

La enumeración más simple que contiene los estados de una solicitud de licencia es:

public enum LeaveRequestState { Submitted, Escalated, Approved }

Podemos hacer referencia a las constantes de esta enumeración:

LeaveRequestState state = LeaveRequestState.Submitted;

Las enumeraciones también pueden contener métodos. Podemos escribir un método abstracto en una enumeración, lo que obligará a cada instancia de enumeración a implementar este método. Esto es muy importante para la implementación de máquinas de estado, como veremos a continuación.

Dado que las enumeraciones de Java extienden implícitamente la clase java.lang.Enum , no pueden extender otra clase. Sin embargo, pueden implementar una interfaz, como cualquier otra clase.

Aquí hay un ejemplo de una enumeración que contiene un método abstracto:

public enum LeaveRequestState { Submitted { @Override public String responsiblePerson() { return "Employee"; } }, Escalated { @Override public String responsiblePerson() { return "Team Leader"; } }, Approved { @Override public String responsiblePerson() { return "Department Manager"; } }; public abstract String responsiblePerson(); }

Tenga en cuenta el uso del punto y coma al final de la última constante de enumeración. Se requiere el punto y coma cuando tenemos uno o más métodos siguiendo las constantes.

En este caso, ampliamos el primer ejemplo con un método responsablePerson () . Esto nos dice la persona responsable de realizar cada acción. Entonces, si intentamos verificar a la persona responsable del estado Escalado , nos dará "Líder de equipo":

LeaveRequestState state = LeaveRequestState.Escalated; assertEquals("Team Leader", state.responsiblePerson());

De la misma forma, si comprobamos quién es el responsable de aprobar la solicitud, nos dará “Jefe de Departamento”:

LeaveRequestState state = LeaveRequestState.Approved; assertEquals("Department Manager", state.responsiblePerson());

3. Máquinas de estado

Una máquina de estados, también llamada máquina de estados finitos o autómata finito, es un modelo computacional que se utiliza para construir una máquina abstracta. Estas máquinas solo pueden estar en un estado en un momento dado. Cada estado es un estado del sistema que cambia a otro estado. Estos cambios de estado se denominan transiciones.

Puede complicarse en matemáticas con diagramas y notaciones, pero las cosas son mucho más fáciles para nosotros los programadores.

State Pattern es uno de los veintitrés patrones de diseño más conocidos del GoF. Este patrón toma prestado el concepto del modelo en matemáticas. Permite que un objeto encapsule diferentes comportamientos para el mismo objeto, según su estado. Podemos programar la transición entre estados y luego definir estados separados.

Para explicar mejor el concepto, ampliaremos nuestro ejemplo de solicitud de permiso para implementar una máquina de estado.

4. Enumeraciones como máquinas de estado

Nos centraremos en la implementación de enumeración de máquinas de estado en Java. Son posibles otras implementaciones y las compararemos en la siguiente sección.

El punto principal de la implementación de la máquina de estados usando una enumeración es que no tenemos que lidiar con establecer explícitamente los estados . En su lugar, podemos simplemente proporcionar la lógica sobre cómo pasar de un estado al siguiente. Vamos a sumergirnos en:

public enum LeaveRequestState { Submitted { @Override public LeaveRequestState nextState() { return Escalated; } @Override public String responsiblePerson() { return "Employee"; } }, Escalated { @Override public LeaveRequestState nextState() { return Approved; } @Override public String responsiblePerson() { return "Team Leader"; } }, Approved { @Override public LeaveRequestState nextState() { return this; } @Override public String responsiblePerson() { return "Department Manager"; } }; public abstract LeaveRequestState nextState(); public abstract String responsiblePerson(); }

En este ejemplo, las transiciones de la máquina de estado se implementan utilizando los métodos abstractos de la enumeración . Más precisamente, usando nextState () en cada constante de enumeración, especificamos la transición al siguiente estado. Si es necesario, también podemos implementar un método previousState () .

A continuación se muestra una prueba para comprobar nuestra implementación:

LeaveRequestState state = LeaveRequestState.Submitted; state = state.nextState(); assertEquals(LeaveRequestState.Escalated, state); state = state.nextState(); assertEquals(LeaveRequestState.Approved, state); state = state.nextState(); assertEquals(LeaveRequestState.Approved, state);

Comenzamos la solicitud de permiso en el estado inicial Enviado . Luego verificamos las transiciones de estado utilizando el método nextState () que implementamos anteriormente.

Tenga en cuenta que dado que Aprobado es el estado final, no puede ocurrir ninguna otra transición .

5. Ventajas de implementar máquinas de estado con enumeraciones de Java

La implementación de máquinas de estado con interfaces y clases de implementación puede ser una cantidad significativa de código para desarrollar y mantener.

Dado que una enumeración de Java es, en su forma más simple, una lista de constantes, podemos usar una enumeración para definir nuestros estados. Y dado que una enumeración también puede contener comportamiento, podemos usar métodos para proporcionar la implementación de transición entre estados.

Tener toda la lógica en una enumeración simple permite una solución limpia y sencilla.

6. Conclusión

En este artículo, analizamos las máquinas de estado y cómo se pueden implementar en Java usando Enums. Dimos un ejemplo y lo probamos.

Finalmente, también discutimos las ventajas de usar enumeraciones para implementar máquinas de estado. Como alternativa a la interfaz y la solución de implementación, las enumeraciones proporcionan una implementación más limpia y más fácil de entender de las máquinas de estado.

Como siempre, todos los fragmentos de código mencionados en este artículo se pueden encontrar en nuestro repositorio de GitHub.