Me han dado un código Java para ver, que simula una carrera de autos, de los cuales incluye una implementación de una máquina de estado básica. Esta no es una máquina de estados de informática clásica, sino simplemente un objeto que puede tener múltiples estados y puede cambiar entre sus estados en función de una serie de cálculos.
Para describir solo el problema, tengo una clase Car, con una clase enum anidada que define algunas constantes para el estado del automóvil (como OFF, IDLE, DRIVE, REVERSE, etc.). Dentro de esta misma clase de automóviles, tengo una función de actualización, que básicamente consiste en una declaración de cambio grande que activa el estado actual de los automóviles, hace algunos cálculos y luego cambia el estado de los automóviles.
Por lo que puedo ver, el estado de Cars solo se usa dentro de su propia clase.
Mi pregunta es, ¿es esta la mejor manera de lidiar con la implementación de una máquina de estado de la naturaleza descrita anteriormente? Suena como la solución más obvia, pero en el pasado siempre he escuchado que "las declaraciones de cambio son malas".
El principal problema que puedo ver aquí es que la declaración de cambio podría volverse muy grande a medida que agregamos más estados (si se considera necesario) y el código podría volverse difícil de manejar y difícil de mantener.
¿Cuál sería una mejor solución para este problema?
fuente
object.state = object.function(object.state);
Respuestas:
Convertí el automóvil en una especie de máquina de estados usando el patrón de estado . Aviso no
switch
oif-then-else
declaraciones se utilizan para la selección del estado.En este caso, todos los estados son clases internas, pero podría implementarse de otra manera.
Cada estado contiene los estados válidos a los que puede cambiar.
Se le solicita al usuario el siguiente estado en caso de que sea posible más de uno, o simplemente para confirmar en caso de que solo uno sea posible.
Puede compilarlo y ejecutarlo para probarlo.
Utilicé un cuadro de diálogo gráfico porque era más fácil de esa manera ejecutarlo interactivamente en Eclipse.
El diagrama UML se toma de aquí .
fuente
Es este tipo de simplificación excesiva lo que da un mal nombre a la programación orientada a objetos. Usar
if
es tan "malo" como usar una instrucción switch. De cualquier manera, no estás despachando polimórficamente.Si debe tener una regla que se ajuste a un sonido, intente esta:
Una declaración de cambio que no está duplicada en ningún otro lugar de la base del código a veces puede no ser malvada. Si los casos no son públicos, pero están encapsulados, en realidad no es asunto de nadie más. Especialmente si sabes cómo y cuándo refactorizarlo en clases. Solo porque puedas no significa que tengas que hacerlo. Es porque puedes hacerlo que es menos crítico hacerlo ahora.
Si se encuentra tratando de meter más y más cosas en la declaración de cambio, difundir el conocimiento de los casos o desear que no sea tan malo hacer una copia, es hora de refactorizar los casos en clases separadas.
Si tiene tiempo para leer más de unas pocas frases sonoras sobre la refactorización de las declaraciones de cambio, c2 tiene una página muy equilibrada sobre el olor de la declaración de cambio .
Incluso en el código OOP, no todos los conmutadores son malos. Es cómo lo estás usando y por qué.
fuente
El automóvil es un tipo de máquina de estados. Las declaraciones de cambio son la forma más sencilla de implementar una máquina de estados que carece de estados superiores y secundarios.
fuente
Las declaraciones de cambio no son malas. ¡No escuche a las personas que dicen cosas como "cambiar las declaraciones son malas"! Algunos usos particulares de las declaraciones de switch son un antipatrón, como usar switch para emular subclases. (¡Pero también puede implementar este antipatrón con if, así que supongo que los if también son malos!).
Su implementación suena bien. Estás en lo correcto es difícil de mantener si agregas muchos más estados. Pero esto no es solo un problema de implementación: tener un objeto con muchos estados con un comportamiento diferente es en sí mismo un problema. Las imágenes de su automóvil tienen 25 estados donde cada uno exhibe un comportamiento diferente y diferentes reglas para las transiciones de estado. Solo especificar y documentar este comportamiento sería una tarea enorme. ¡Tendrás miles de reglas de transición de estado! El tamaño del
switch
sería solo un síntoma de un problema mayor. Entonces, si es posible, evite seguir por este camino.Un posible remedio es dividir el estado en subestados independientes. Por ejemplo, ¿REVERSE es realmente un estado distinto de DRIVE? Quizás los estados del automóvil podrían dividirse en dos: estado del motor (APAGADO, INACTIVO, CONDUCCIÓN) y dirección (ADELANTE, REVERSA). El estado y la dirección del motor probablemente serán en su mayoría independientes, por lo que reduce la duplicación lógica y las reglas de transición de estado. Más objetos con menos estados son mucho más fáciles de administrar que un solo objeto con numerosos estados.
fuente
En su ejemplo, los automóviles son simplemente máquinas de estado en el sentido clásico de la informática. Tienen un conjunto de estados pequeño y bien definido y algún tipo de lógica de transición de estado.
Mi primera sugerencia es considerar dividir la lógica de transición en su propia función (o clase, si su idioma no admite funciones de primera clase).
Mi segunda sugerencia es considerar romper la lógica de transición en el estado mismo, que tendría su propia función (o clase, si su lenguaje no admite funciones de primera clase).
En cualquier esquema, el proceso para el estado de transición se vería así:
o
El segundo podría, por supuesto, trivialmente ser envuelto en la clase de autos para parecerse al primero.
En ambos escenarios, agregar un nuevo estado (por ejemplo, DRAFTING) solo implicaría agregar un nuevo tipo de objeto de estado y cambiar los objetos que cambian específicamente al nuevo estado.
fuente
Depende de qué tan grande
switch
pueda ser.En su ejemplo, creo que a
switch
está bien, ya que no hay ningún otro estado en el que pueda pensar queCar
pueda tener, por lo que no se agrandaría con el tiempo.Si el único problema es tener un interruptor grande donde cada uno
case
tiene muchas instrucciones, simplemente haga métodos privados distintos para cada uno.A veces, las personas sugieren el patrón de diseño del estado , pero es más apropiado cuando se trata de una lógica compleja y estados que toman decisiones comerciales diferentes para muchas operaciones distintas. De lo contrario, los problemas simples deberían tener soluciones simples.
En algunos escenarios, podría tener métodos que solo realicen tareas cuando el estado es A o B, pero no C o D, o tener métodos múltiples con operaciones muy simples que dependen del estado. Entonces una o varias
switch
declaraciones serían mejores.fuente
Esto suena como una máquina de estado de la vieja escuela del tipo que se usó antes de que nadie hiciera programación orientada a objetos, y mucho menos a Design Patterns. Se puede implementar en cualquier lenguaje que tenga instrucciones de cambio, como C.
Como han dicho otros, no hay nada intrínsecamente malo con las declaraciones de cambio. Las alternativas son a menudo más complicadas y más difíciles de entender.
A menos que el número de casos de cambio se vuelva ridículamente grande, la cosa puede seguir siendo bastante manejable. El primer paso para mantenerlo legible es reemplazar el código en cada caso con una llamada de función para implementar el comportamiento del estado.
fuente