State
Permite que um objeto altere seu comportamento quando seu estado interno muda. O objeto vai parecer mudar de classe.
Intenção
Encapsular comportamento específico de estado em objetos de estado separados e delegar o comportamento ao estado atual, para que o objeto principal mude seu comportamento dinamicamente conforme suas transições de estado, sem recorrer a grandes blocos condicionais.
Problema
Um objeto precisa mudar de comportamento com base no seu estado interno, e o número de estados só cresce. O código fica repleto de condicionais que checam o estado atual antes de cada operação, tornando-o frágil e difícil de estender com novos estados sem mexer na lógica existente.
Solução
Crie uma interface State que declara os métodos cujo comportamento varia por estado. Implemente uma classe ConcreteState para cada estado distinto. O Context mantém uma referência ao objeto de estado atual e delega o trabalho dependente de estado a ele. Transições de estado são feitas trocando o objeto de estado atual, seja pelo Context ou pelos próprios objetos de estado.
Participantes
- State — define uma interface para encapsular o comportamento associado a um estado particular do Context
- ConcreteState — cada subclasse implementa o comportamento associado a um estado do Context
- Context — mantém uma referência ao ConcreteState atual e delega o comportamento específico de estado a ele
Vantagens
- Localiza comportamento específico de estado e particiona comportamento de diferentes estados em classes separadas
- Torna as transições de estado explícitas, substituindo lógica condicional por dispatch polimórfico
- Novos estados podem ser adicionados sem modificar o Context ou outras classes de estado
- Elimina condicionais monolíticos que testam o estado atual em múltiplas operações
Desvantagens
- Aumenta o número de classes, o que pode parecer pesado para objetos com poucos estados
- Objetos de estado podem precisar de acesso a internals do Context, o que pode apertar o acoplamento
- Se as transições de estado estiverem espalhadas por vários estados concretos, o fluxo geral pode ser difícil de acompanhar
Analogia do mundo real
Pense em um semáforo. Ele passa por três estados — verde, amarelo, e vermelho — e seu comportamento (se os carros param, desaceleram, ou seguem) depende inteiramente do estado atual. Cada estado sabe quanto tempo dura e qual estado vem a seguir. O semáforo em si não contém um bloco gigante de if-else; em vez disso, a fase atual do sinal dita o comportamento.
Casos de uso
- Gerenciamento de ciclo de vida de pedidos — rascunho, enviado, pago, despachado, entregue, cancelado
- Fluxo de trabalho de documentos — rascunho, revisão, aprovado, publicado, arquivado
- Controles de player de mídia — parado, tocando, pausado
- Estados de conexão TCP — listen, established, closed
- Operações de máquina de venda automática — ociosa, aceitando moedas, entregando, sem estoque
- Estados de personagem de jogo — parado, andando, correndo, pulando, atacando
Exemplos de código
interface DocumentState {
String name();
void edit(DocumentContext doc, String text);
void submit(DocumentContext doc);
void approve(DocumentContext doc);
void reject(DocumentContext doc);
}
final class DocumentContext {
private DocumentState state = new DraftState();
String content = "";
void setState(DocumentState state) {
System.out.println(" [Transition] " + this.state.name() + " -> " + state.name());
this.state = state;
}
String getStateName() { return state.name(); }
void edit(String text) { state.edit(this, text); }
void submit() { state.submit(this); }
void approve() { state.approve(this); }
void reject() { state.reject(this); }
}
final class DraftState implements DocumentState {
@Override
public String name() { return "Draft"; }
@Override
public void edit(DocumentContext doc, String text) {
doc.content = text;
}
@Override
public void submit(DocumentContext doc) {
doc.setState(new ReviewState());
}
@Override
public void approve(DocumentContext doc) {
System.out.println("Cannot approve a draft");
}
@Override
public void reject(DocumentContext doc) {
System.out.println("Cannot reject a draft");
}
}
final class ReviewState implements DocumentState {
@Override
public String name() { return "Review"; }
@Override
public void edit(DocumentContext doc, String text) {
System.out.println("Cannot edit while in review");
}
@Override
public void submit(DocumentContext doc) {
System.out.println("Already submitted");
}
@Override
public void approve(DocumentContext doc) {
doc.setState(new ApprovedState());
}
@Override
public void reject(DocumentContext doc) {
doc.setState(new DraftState());
}
}
final class ApprovedState implements DocumentState {
@Override
public String name() { return "Approved"; }
@Override
public void edit(DocumentContext doc, String text) {
System.out.println("Cannot edit an approved document");
}
@Override
public void submit(DocumentContext doc) {
System.out.println("Already approved");
}
@Override
public void approve(DocumentContext doc) {
System.out.println("Already approved");
}
@Override
public void reject(DocumentContext doc) {
System.out.println("Cannot reject an approved document");
}
}
public class StateDemo {
public static void main(String[] args) {
DocumentContext doc = new DocumentContext();
doc.edit("Draft content");
doc.submit(); // Draft -> Review
doc.approve(); // Review -> Approved
System.out.println(doc.getStateName()); // Approved
}
}Um fluxo de trabalho de documento onde o DocumentContext delega ações de editar, enviar, aprovar, e rejeitar ao seu objeto de estado atual. Cada estado controla quais transições são permitidas.
Padrões relacionados
Strategy
Define uma família de algoritmos, encapsula cada um deles, e os torna intercambiáveis. Strategy permite que o algoritmo varie independentemente dos clientes que o usam.