Padrão State
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 60 de 70.
O padrão State é um padrão comportamental que permite que um objeto altere seu comportamento quando seu estado interno muda. Em vez de usar declarações condicionais complexas para lidar com diferentes estados, você encapsula cada estado em sua própria classe e delega o comportamento ao objeto de estado atual.
O padrão consiste em três componentes: uma interface de estado que define o comportamento específico do estado, classes de estado concretas que implementam esse comportamento e uma classe de contexto que mantém uma referência ao estado atual:
public interface IDocumentState
{
void Publish(Document doc);
}
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}O contexto mantém o estado atual e delega ações a ele. Objetos de estado podem disparar transições chamando SetState() no contexto:
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
var doc = new Document();
doc.Publish(); // Movendo para moderação
doc.Publish(); // Publicando documento
doc.Publish(); // Já publicadoO padrão State elimina extensas instruções if-else ou switch que verificam o estado atual. Cada classe de estado lida apenas com seu próprio comportamento, tornando o código mais fácil de manter e estender com novos estados.
Desafio
FácilVamos construir um sistema de semáforo usando o padrão State. Os semáforos alternam entre estados distintos (vermelho, amarelo, verde), e cada estado determina o que acontece quando a luz muda. Em vez de usar condicionais complexas, você encapsulará cada estado da luz em sua própria classe.
Você organizará seu código em três arquivos:
TrafficLightState.cs: Defina uma interfaceITrafficLightStateno namespaceTrafficcom um métodoChange(TrafficLight light). Em seguida, crie três classes de estado concretas:RedState- quandoChangeé chamado, ele imprimeRed - Stop! Changing to Green...e faz a transição do semáforo paraGreenStateGreenState- imprimeGreen - Go! Changing to Yellow...e faz a transição paraYellowStateYellowState- imprimeYellow - Caution! Changing to Red...e faz a transição paraRedState
TrafficLight.cs: Crie uma classeTrafficLightno mesmo namespace. Esta é a sua classe de contexto que mantém o estado atual. Ela deve começar emRedStatepor padrão e ter:- Um método
SetState(ITrafficLightState state)que os objetos de estado usam para disparar transições - Um método
Change()que delega para o métodoChangedo estado atual
- Um método
Program.cs: Reúna tudo criando umTrafficLighte alternando entre as mudanças de estado com base na entrada.
Você receberá uma entrada:
- O número de vezes para mudar o semáforo (um número inteiro)
Crie um TrafficLight (que começa no vermelho) e chame Change() o número especificado de vezes.
Por exemplo, se a entrada for 3, a saída deve ser:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...Se a entrada for 5, o semáforo reinicia o ciclo:
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...
Yellow - Caution! Changing to Red...
Red - Stop! Changing to Green...
Green - Go! Changing to Yellow...Observe como a classe TrafficLight não contém lógica sobre o que cada cor significa ou o que vem a seguir - todo esse conhecimento reside nas classes de estado individuais. Cada estado é responsável por seu próprio comportamento e por disparar a transição para o próximo estado!
Folha de consulta
O padrão State é um padrão comportamental que permite que um objeto altere seu comportamento quando seu estado interno muda. Em vez de usar instruções condicionais complexas, você encapsula cada estado em sua própria classe e delega o comportamento ao objeto de estado atual.
O padrão consiste em três componentes:
- Interface State: Define o comportamento específico do estado
- Classes de estado concreto: Implementam o comportamento para cada estado
- Classe Context: Mantém uma referência ao estado atual
Exemplo de implementação:
// Interface State
public interface IDocumentState
{
void Publish(Document doc);
}
// Classes de estado concreto
public class DraftState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Moving to moderation");
doc.SetState(new ModerationState());
}
}
public class ModerationState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Publishing document");
doc.SetState(new PublishedState());
}
}
public class PublishedState : IDocumentState
{
public void Publish(Document doc)
{
Console.WriteLine("Already published");
}
}
// Classe Context
public class Document
{
private IDocumentState _state = new DraftState();
public void SetState(IDocumentState state) => _state = state;
public void Publish() => _state.Publish(this);
}
// Uso
var doc = new Document();
doc.Publish(); // Moving to moderation
doc.Publish(); // Publishing document
doc.Publish(); // Already publishedO contexto mantém o estado atual e delega ações a ele. Objetos de estado podem disparar transições chamando SetState() no contexto. Isso elimina instruções if-else ou switch extensas, tornando o código mais fácil de manter e estender com novos estados.
Experimente você mesmo
using System;
using Traffic;
class Program
{
public static void Main(String[] args)
{
// Leia o número de vezes para mudar o semáforo
int n = Convert.ToInt32(Console.ReadLine());
// TODO: Crie uma instância de TrafficLight (inicia em RedState por padrão)
// TODO: Chame o método Change() n vezes usando um loop
}
}
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 ExternosNamespaces e DiretivasIntrodução a Classes e ObjetosA Palavra-chave 'this'Métodos e ParâmetrosCampos vs PropriedadesConstrutoresInicializadores de ObjetosRecapitulação - Calculadora Simples4Herança
Sintaxe Básica de Herança (:)A Palavra-chave 'base'Palavras-chave Virtual e OverrideClasses SeladasA Classe Base 'object'Recapitulação - Hierarquia de Funcionários7Recursos Avançados
Sobrecarga de OperadoresIndexadores (this[])Sobrescrita de ToString()Métodos de ExtensãoRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoSingleton Thread-SafePadrão FactoryPadrão Observer (Eventos)Padrão Strategy2Propriedades e Membros Estáticos
Propriedades AutoimplementadasPropriedades de Apenas Leitura/EscritaCampos e Métodos EstáticosClasses EstáticasMembros com Corpo de Expressão5Polimorfismo e Interfaces
Polimorfismo: Compilação vs ExecuçãoInterface vs Classe AbstrataMúltiplas InterfacesInterfaces ExplícitasUpcasting e DowncastingRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição sobre HerançaGenerics (Classes e Métodos)Delegates e EventosAtributos e ReflectionIDisposable e a Instrução usingFundamentos de Injeção de Dependência11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Arquitetura de Classes
Dados de Instância vs EstáticosPalavras-chave 'readonly' e 'const'Campos de SuporteRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Modificadores de AcessoPropriedades para EncapsulamentoImplementação de Ocultação de DadosPadrões de ImutabilidadeRecapitulação - Registros de Estudantes9Argumentos Variáveis
A Palavra-chave 'params'Parâmetros OpcionaisArgumentos NomeadosSobrecarga de Métodos