Introducción al Patrón State

El patrón State es un patrón de diseño de comportamiento que permite a un objeto cambiar su comportamiento cuando su estado interno cambia. Este patrón es particularmente útil cuando un objeto debe cambiar su comportamiento en tiempo de ejecución dependiendo de su estado.

Conceptos Clave

  • Contexto: El objeto que contiene el estado y cuyo comportamiento cambia.
  • Estado: Una interfaz o clase abstracta que define el comportamiento asociado con un estado particular del contexto.
  • Estados Concretos: Implementaciones específicas de la interfaz de estado que definen el comportamiento para un estado particular del contexto.

Ejemplo Práctico

Vamos a implementar un ejemplo sencillo de una máquina expendedora que cambia su comportamiento basado en su estado (sin moneda, con moneda, vendiendo producto).

Diagrama de Clases

Clase Descripción
VendingMachine Clase Contexto que mantiene una referencia al estado actual.
State Interfaz que define el comportamiento común para todos los estados.
NoCoinState Estado concreto que representa la máquina sin moneda.
HasCoinState Estado concreto que representa la máquina con una moneda insertada.
SoldState Estado concreto que representa la máquina vendiendo un producto.

Código

Interfaz State

public interface State {
    void insertCoin();
    void ejectCoin();
    void pressButton();
    void dispense();
}

Clase Contexto: VendingMachine

public class VendingMachine {
    private State noCoinState;
    private State hasCoinState;
    private State soldState;
    
    private State currentState;
    
    public VendingMachine() {
        noCoinState = new NoCoinState(this);
        hasCoinState = new HasCoinState(this);
        soldState = new SoldState(this);
        
        currentState = noCoinState; // Estado inicial
    }
    
    public void setState(State state) {
        currentState = state;
    }
    
    public State getNoCoinState() {
        return noCoinState;
    }
    
    public State getHasCoinState() {
        return hasCoinState;
    }
    
    public State getSoldState() {
        return soldState;
    }
    
    public void insertCoin() {
        currentState.insertCoin();
    }
    
    public void ejectCoin() {
        currentState.ejectCoin();
    }
    
    public void pressButton() {
        currentState.pressButton();
        currentState.dispense();
    }
}

Estados Concretos

NoCoinState
public class NoCoinState implements State {
    private VendingMachine vendingMachine;
    
    public NoCoinState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }
    
    @Override
    public void insertCoin() {
        System.out.println("Coin inserted.");
        vendingMachine.setState(vendingMachine.getHasCoinState());
    }
    
    @Override
    public void ejectCoin() {
        System.out.println("No coin to eject.");
    }
    
    @Override
    public void pressButton() {
        System.out.println("Insert coin first.");
    }
    
    @Override
    public void dispense() {
        System.out.println("No coin inserted.");
    }
}
HasCoinState
public class HasCoinState implements State {
    private VendingMachine vendingMachine;
    
    public HasCoinState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }
    
    @Override
    public void insertCoin() {
        System.out.println("Coin already inserted.");
    }
    
    @Override
    public void ejectCoin() {
        System.out.println("Coin ejected.");
        vendingMachine.setState(vendingMachine.getNoCoinState());
    }
    
    @Override
    public void pressButton() {
        System.out.println("Button pressed.");
        vendingMachine.setState(vendingMachine.getSoldState());
    }
    
    @Override
    public void dispense() {
        System.out.println("No product dispensed.");
    }
}
SoldState
public class SoldState implements State {
    private VendingMachine vendingMachine;
    
    public SoldState(VendingMachine vendingMachine) {
        this.vendingMachine = vendingMachine;
    }
    
    @Override
    public void insertCoin() {
        System.out.println("Please wait, we're already giving you a product.");
    }
    
    @Override
    public void ejectCoin() {
        System.out.println("Sorry, you already pressed the button.");
    }
    
    @Override
    public void pressButton() {
        System.out.println("Button already pressed.");
    }
    
    @Override
    public void dispense() {
        System.out.println("Dispensing product.");
        vendingMachine.setState(vendingMachine.getNoCoinState());
    }
}

Ejercicio Práctico

Ejercicio

Implementa una máquina de café que tiene los siguientes estados: NoCoinState, HasCoinState, BrewingState, y SoldOutState. La máquina debe permitir insertar una moneda, devolver la moneda, seleccionar un tipo de café y dispensar el café.

Solución

  1. Define la interfaz State con los métodos necesarios.
  2. Implementa la clase CoffeeMachine que actúa como el contexto.
  3. Implementa los estados concretos (NoCoinState, HasCoinState, BrewingState, SoldOutState).

Retroalimentación y Errores Comunes

  • Error Común: No cambiar el estado del contexto después de una acción.

    • Solución: Asegúrate de que cada acción en un estado concreto cambie el estado del contexto si es necesario.
  • Error Común: Implementar lógica específica del estado en el contexto.

    • Solución: Mantén la lógica específica del estado dentro de las clases de estado concreto.

Conclusión

El patrón State es una poderosa herramienta para manejar cambios en el comportamiento de un objeto basado en su estado interno. Al separar los estados en clases concretas, se mejora la mantenibilidad y extensibilidad del código. En el siguiente módulo, exploraremos el patrón Strategy, que permite definir una familia de algoritmos y hacer que sean intercambiables.

© Copyright 2024. Todos los derechos reservados