Menu
Coddy logo textTech

Patrón State

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 100 de 104.

El patrón State permite que un objeto cambie su comportamiento cuando cambia su estado interno, haciendo que parezca como si el objeto hubiera cambiado de clase. En lugar de utilizar sentencias condicionales complejas para manejar diferentes estados, se encapsula cada estado como una clase separada.

El patrón consiste en un Contexto que mantiene una referencia al estado actual, una interfaz de Estado que define el comportamiento específico del estado y Estados Concretos que implementan el comportamiento para cada estado:

#include <iostream>
#include <memory>

class Document;  // Declaración adelantada

// Interfaz de estado
class DocumentState {
public:
    virtual void publish(Document& doc) = 0;
    virtual std::string getName() const = 0;
    virtual ~DocumentState() = default;
};

// Contexto
class Document {
    std::unique_ptr<DocumentState> state;
public:
    Document();
    void setState(std::unique_ptr<DocumentState> newState) {
        state = std::move(newState);
    }
    void publish() { state->publish(*this); }
    std::string getStateName() const { return state->getName(); }
};

// Estados concretos
class Draft : public DocumentState {
public:
    void publish(Document& doc) override;
    std::string getName() const override { return "Draft"; }
};

class Review : public DocumentState {
public:
    void publish(Document& doc) override;
    std::string getName() const override { return "Review"; }
};

class Published : public DocumentState {
public:
    void publish(Document& doc) override {
        std::cout << "Already published\n";
    }
    std::string getName() const override { return "Published"; }
};

void Draft::publish(Document& doc) {
    std::cout << "Moving to review\n";
    doc.setState(std::make_unique<Review>());
}

void Review::publish(Document& doc) {
    std::cout << "Publishing document\n";
    doc.setState(std::make_unique<Published>());
}

Document::Document() : state(std::make_unique<Draft>()) {}

Cada estado maneja la acción publish() de manera diferente y es responsable de la transición al siguiente estado. El Document no necesita conocer la lógica de transición; simplemente delega al estado actual. Esto elimina las sentencias switch grandes y hace que añadir nuevos estados sea sencillo.

Utiliza State cuando el comportamiento de un objeto depende en gran medida de su estado y tienes muchas sentencias condicionales que cambian según el estado del objeto.

challenge icon

Desafío

Fácil

Vamos a construir un Controlador de Semáforo utilizando el patrón State. Crearás un sistema donde un semáforo cicla a través de diferentes estados (Red, Yellow, Green), con cada estado determinando qué sucede cuando la luz cambia y qué mensaje se muestra. Este es un ejemplo clásico del patrón State: el comportamiento del semáforo depende enteramente de su estado actual.

Organizarás tu código en tres archivos:

  • TrafficLightState.h: Define la interfaz de estado y todas las clases de estado concretas.

    Crea una clase abstracta TrafficLightState con:

    • Un método virtual puro change(TrafficLight& light) que maneja la transición al siguiente estado
    • Un método virtual puro getColor() que devuelve el color actual como un string
    • Un método virtual puro getAction() que devuelve lo que los conductores deben hacer (por ejemplo, "Stop", "Caution", "Go")
    • Un destructor virtual

    Implementa tres estados concretos:

    • RedState — el color es "Red", la acción es "Stop", cambia a Green
    • YellowState — el color es "Yellow", la acción es "Caution", cambia a Red
    • GreenState — el color es "Green", la acción es "Go", cambia a Yellow

    Necesitarás una declaración anticipada (forward declaration) para TrafficLight ya que los estados hacen referencia a ella.

  • TrafficLight.h: Crea la clase de contexto que mantiene el estado actual.

    Tu clase TrafficLight debe contener un std::unique_ptr<TrafficLightState> e iniciar en el estado Red por defecto. Implementa:

    • setState(std::unique_ptr<TrafficLightState> newState) — cambia el estado actual
    • change() — delega al método change del estado actual
    • display() — imprime el estado del semáforo en el formato: [Color]: [Action]
  • main.cpp: Demuestra el ciclo del semáforo a través de los estados.

    Lee una entrada: el número de cambios de estado a realizar (entero).

    Crea un TrafficLight y muestra su estado inicial. Luego realiza el número especificado de cambios, mostrando el estado después de cada cambio.

Por ejemplo, con la entrada 3:

Red: Stop
Green: Go
Yellow: Caution
Red: Stop

Con la entrada 6:

Red: Stop
Green: Go
Yellow: Caution
Red: Stop
Green: Go
Yellow: Caution
Red: Stop

Observa cómo el semáforo cicla a través de sus estados en un patrón predecible: Red → Green → Yellow → Red. Cada estado sabe qué estado sigue y maneja su propia transición. La clase TrafficLight no necesita ninguna lógica condicional para determinar qué sucede; simplemente delega a cualquier estado en el que se encuentre actualmente.

Hoja de referencia

El patrón State permite que un objeto cambie su comportamiento cuando su estado interno cambia. En lugar de utilizar sentencias condicionales complejas, cada estado se encapsula como una clase separada.

El patrón consta de tres componentes:

  • Interfaz State: Define el comportamiento específico del estado
  • Estados Concretos: Implementan el comportamiento para cada estado
  • Contexto: Mantiene una referencia al estado actual

Estructura básica:

#include <memory>

class Context;  // Declaración anticipada

// Interfaz State
class State {
public:
    virtual void handleAction(Context& ctx) = 0;
    virtual std::string getName() const = 0;
    virtual ~State() = default;
};

// Contexto
class Context {
    std::unique_ptr<State> state;
public:
    void setState(std::unique_ptr<State> newState) {
        state = std::move(newState);
    }
    void performAction() { 
        state->handleAction(*this); 
    }
    std::string getStateName() const { 
        return state->getName(); 
    }
};

// Estado Concreto
class ConcreteState : public State {
public:
    void handleAction(Context& ctx) override {
        // Manejar la acción y transicionar al siguiente estado
        ctx.setState(std::make_unique<AnotherState>());
    }
    std::string getName() const override { 
        return "ConcreteState"; 
    }
};

Cada estado maneja las acciones de manera diferente y es responsable de la transición al siguiente estado. El Contexto delega el comportamiento al estado actual sin necesidad de conocer la lógica de transición.

Utilice State cuando el comportamiento de un objeto dependa en gran medida de su estado y desee evitar grandes sentencias condicionales.

Pruébalo tú mismo

#include <iostream>
#include "TrafficLight.h"

using namespace std;

int main() {
    // Lee el número de cambios de estado
    int numChanges;
    cin >> numChanges;
    
    // TODO: Crea un objeto TrafficLight
    
    // TODO: Muestra el estado inicial
    
    // TODO: Realiza el número especificado de cambios
    // Después de cada cambio, muestra el estado actual
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos