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 parecer 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 Contexto que mantém uma referência ao estado atual, uma interface de Estado que define o comportamento específico de cada estado e Estados Concretos que implementam o comportamento de 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 fazer a transição para o próximo estado. O Document não precisa conhecer a lógica de transição — ele simplesmente delega ao estado atual. Isso elimina grandes instruções switch e torna simples adicionar novos estados.
Use State quando o comportamento de um objeto depende muito do seu estado e você tem muitas instruções condicionais que alternam com base no estado do objeto.
Desafio
FácilVamos criar um Controlador de Semáforo usando o padrão State. Você criará um sistema no qual um semáforo alterna entre diferentes estados (Red, Yellow, Green), com cada estado determinando o que acontece quando o sinal 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: Defina a interface do estado e todas as classes de estado concretas.Crie uma classe abstrata
TrafficLightStatecom:- Um método virtual puro
change(TrafficLight& light)que gerencia 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 (por exemplo, "Stop", "Caution", "Go") - Um destrutor virtual
Implemente três estados concretos:
RedState: a cor é"Red", a ação é"Stop"e muda para GreenYellowState: a cor é"Yellow", a ação é"Caution"e muda para RedGreenState: a cor é"Green", a ação é"Go"e muda para Yellow
Você precisará de uma declaração antecipada para
TrafficLight, pois os estados fazem referência a ela.- Um método virtual puro
TrafficLight.h: Crie a classe de contexto que mantém o estado atual.Sua classe
TrafficLightdeve conter umstd::unique_ptr<TrafficLightState>e começar no estado Red por padrão. Implemente:setState(std::unique_ptr<TrafficLightState> newState): altera o estado atualchange(): delega para o método change do estado atualdisplay(): 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 realizar (inteiro).
Crie um
TrafficLighte 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: StopCom a entrada 6:
Red: Stop
Green: Go
Yellow: Caution
Red: Stop
Green: Go
Yellow: Caution
Red: StopObserve como o semáforo alterna entre seus estados em um padrão previsível: Red → Green → Yellow → Red. Cada estado sabe qual estado vem em seguida e gerencia 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 o estado em que se encontra atualmente.
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: Crie um objeto TrafficLight
// TODO: Exiba o estado inicial
// TODO: Realize o número especificado de mudanças
// Após cada mudança, exiba o estado atual
return 0;
}
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
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online