El patrón State permite a un objeto alterar su comportamiento cuando su estado interno cambia.
El patrón encapsula los estados variables como objetos de estado independientes, separando las preocupaciones del estado y el comportamiento.
Ante un cambio de estado, el objeto parecerá cambiar su clase.
El patrón State trata los siguientes problemas:
Comportamiento Basado en el Estado: Maneja situaciones donde el comportamiento de un objeto depende de su estado, y debe cambiar en tiempo de ejecución.
Condiciones Complejas: Evita el uso de múltiples condicionales para determinar el comportamiento de un objeto basado en su estado.
El patrón State propone las siguientes soluciones:
Objetos de Estado: Encapsula los diferentes estados de un objeto en clases de estado separadas, cada una implementando un comportamiento específico.
Contexto: Un objeto que mantiene una referencia a un objeto de estado que representa su estado actual. El contexto delega el comportamiento relacionado con el estado a los objetos de estado.
Este patrón es recomendable cuando:
Separación de Responsabilidades: Separa el código relacionado con el estado del objeto, del resto del comportamiento del objeto.
Organización y Mantenibilidad: Facilita la organización del código y su mantenimiento, especialmente en sistemas complejos.
Flexibilidad para Cambios: Permite cambiar fácilmente el comportamiento del objeto agregando nuevos estados.
Aumento de la Complejidad: Agrega varias clases y objetos adicionales.
Número de Clases: Cuánto más estados, más clases, por lo que pueden crecer considerablemente.
Dependencia de Contexto y Estado: La comunicación entre el contexto y los objetos de estado puede volverse compleja.
Debemos crear un sistema de control de semáforos, algo crítico para regular el tráfico. Deben manejar varios estados (Rojo, Amarillo, Verde) de manera eficiente y fiable.
El desafío está en gestionar los cambios de estado del semáforo de una manera que sea fácil de entender, mantener y expandir.
Un semáforo no solo debe cambiar entre los estados de Rojo, Amarillo y Verde, sino también hacerlo de manera que respete las reglas de tráfico y seguridad.
Implementar esta lógica de cambio de estado directamente dentro de la clase del semáforo puede llevar a un código complejo y difícil de mantener (lleno de condiciones).
Decidimos usar el patrón State porque nos permite encapsular los comportamientos asociados con los diferentes estados del semáforo en clases separadas.
Esto simplifica la lógica en la clase principal del semáforo y hace que el sistema sea más flexible y fácil de modificar o expandir.
Por ejemplo, si las reglas de tráfico cambian o se introducen nuevos tipos de semáforos, podemos agregar o modificar los estados correspondientes sin alterar el núcleo de la lógica del semáforo.
Definimos la interfaz TrafficLightState para manejar los estados del semáforo. Creamos los estados concretos (Red, Yellow, Green), y el Context que mantiene el estado actual y delega los cambios de estado al State. Finalmente, en el cliente vamos cambiando de estado cada cierto tiempo, simulando un semáforo.
Codificamos en Java lo que preparamos en el diagrama.
Definimos la interfaz State:
interface TrafficLightState {
void handleState(TrafficLightContext context);
}
Creamos los concrete States:
class GreenState implements TrafficLightState {
public void handleState(TrafficLightContext context) {
System.out.println("Luz verde: El tráfico puede avanzar.");
context.setState(new YellowState());
}
}
class YellowState implements TrafficLightState {
public void handleState(TrafficLightContext context) {
System.out.println("Luz amarilla: Precaución.");
context.setState(new RedState());
}
}
class RedState implements TrafficLightState {
public void handleState(TrafficLightContext context) {
System.out.println("Luz roja: Detener el tráfico.");
context.setState(new GreenState());
}
}
Implementamos el Context:
class TrafficLightContext {
private TrafficLightState state;
public TrafficLightContext() {
state = new RedState(); // Estado inicial
}
void setState(TrafficLightState state) {
this.state = state;
}
void change() {
state.handleState(this);
}
}
El código cliente:
public class TrafficManagementSystem {
public static void main(String[] args) {
TrafficLightContext trafficLight = new TrafficLightContext();
for (int i = 0; i < 6; i++) {
trafficLight.change();
try {
Thread.sleep(2000); // Simular tiempo de espera entre cambios
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Los participantes que vimos antes son: Context, State, ConcreteState, Client:
En este ejemplo de uso del patrón State pudimos ver una gestión flexible de los diferentes estados de luz del semáforo, simplificando cambios y ampliaciones futuras.
Aunque incrementó el número de clases y podría introducir complejidad en sistemas más simples, eliminó la necesidad de condicionales complicados e hizo que el código sea fácil de mantener.
Strategy permite cambiar el comportamiento del objeto dinámicamente, State permite cambiar el comportamiento de acuerdo a su estado interno.