Menu
Coddy logo textTech

Padrão State

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 100 de 104.

O padrão State permite que um objeto altere seu comportamento quando seu estado interno muda, fazendo com que pareça que o objeto mudou de classe. Em vez de usar instruções condicionais complexas para lidar com diferentes estados, você encapsula cada estado como uma classe separada.

O padrão consiste em um Context que mantém uma referência ao estado atual, uma interface State que define o comportamento específico do estado e Concrete States que implementam o comportamento para cada estado:

#include <iostream>
#include <memory>

class Document;  // Declaração antecipada

// Interface 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 lida com a ação publish() de forma diferente e é responsável por transitar para o próximo estado. O Document não precisa conhecer a lógica de transição - ele simplesmente delega para o estado atual. Isso elimina grandes instruções switch e torna a adição de novos estados direta.

Use o State quando o comportamento de um objeto depende fortemente de seu estado e você tem muitas instruções condicionais que alternam com base no estado do objeto.

challenge icon

Desafio

Fácil

Vamos construir um Controlador de Semáforo usando o padrão State. Você criará um sistema onde um semáforo alterna entre diferentes estados (Red, Yellow, Green), com cada estado determinando o que acontece quando a luz muda e qual mensagem é exibida. Este é um exemplo clássico do padrão State — o comportamento do semáforo depende inteiramente do seu estado atual.

Você organizará seu código em três arquivos:

  • TrafficLightState.h: Define a interface de estado e todas as classes de estado concretas.

    Crie uma classe abstrata TrafficLightState com:

    • Um método virtual puro change(TrafficLight& light) que lida com a transição para o próximo estado
    • Um método virtual puro getColor() que retorna a cor atual como uma string
    • Um método virtual puro getAction() que retorna o que os motoristas devem fazer (ex: "Stop", "Caution", "Go")
    • Um destrutor virtual

    Implemente três estados concretos:

    • RedState — a cor é "Red", a ação é "Stop", muda para Green
    • YellowState — a cor é "Yellow", a ação é "Caution", muda para Red
    • GreenState — a cor é "Green", a ação é "Go", muda para Yellow

    Você precisará de uma declaração antecipada (forward declaration) para TrafficLight, já que os estados fazem referência a ela.

  • TrafficLight.h: Crie a classe de contexto que mantém o estado atual.

    Sua classe TrafficLight deve conter um std::unique_ptr<TrafficLightState> e começar no estado Red por padrão. Implemente:

    • setState(std::unique_ptr<TrafficLightState> newState) — altera o estado atual
    • change() — delega para o método change do estado atual
    • display() — imprime o status do semáforo no formato: [Color]: [Action]
  • main.cpp: Demonstre o semáforo alternando entre os estados.

    Leia uma entrada: o número de mudanças de estado a serem realizadas (inteiro).

    Crie um TrafficLight e exiba seu estado inicial. Em seguida, realize o número especificado de mudanças, exibindo o estado após cada mudança.

Por exemplo, com a entrada 3:

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

Com a entrada 6:

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

Observe como o semáforo alterna entre seus estados em um padrão previsível: Red → Green → Yellow → Red. Cada estado sabe qual estado vem a seguir e lida com sua própria transição. A classe TrafficLight não precisa de nenhuma lógica condicional para determinar o que acontece — ela simplesmente delega para qualquer estado em que esteja atualmente.

Folha de consulta

O padrão State permite que um objeto altere seu comportamento quando seu estado interno muda. Em vez de usar instruções condicionais complexas, cada estado é encapsulado como uma classe separada.

O padrão consiste em três componentes:

  • Interface State: Define o comportamento específico do estado
  • Estados Concretos: Implementam o comportamento para cada estado
  • Contexto: Mantém uma referência ao estado atual

Estrutura básica:

#include <memory>

class Context;  // Forward declaration

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

// Context
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(); 
    }
};

// Concrete State
class ConcreteState : public State {
public:
    void handleAction(Context& ctx) override {
        // Handle action and transition to next state
        ctx.setState(std::make_unique<AnotherState>());
    }
    std::string getName() const override { 
        return "ConcreteState"; 
    }
};

Cada estado manipula as ações de forma diferente e é responsável pela transição para o próximo estado. O Contexto delega o comportamento ao estado atual sem precisar conhecer a lógica de transição.

Use o State quando o comportamento de um objeto depende fortemente de seu estado e você deseja evitar grandes instruções condicionais.

Experimente você mesmo

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

using namespace std;

int main() {
    // Lê o número de mudanças de estado
    int numChanges;
    cin >> numChanges;
    
    // TODO: Criar um objeto TrafficLight
    
    // TODO: Exibir o estado inicial
    
    // TODO: Realizar o número especificado de mudanças
    // Após cada mudança, exiba o estado atual
    
    return 0;
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos