GoF, p. 305 também chamado de Objects for States
Estado
State
O objeto parece mudar de classe quando seu estado interno muda.
Permitir que um objeto altere seu comportamento quando seu estado interno muda. O objeto parecerá ter mudado de classe.
O botão do celular
O mesmo botão lateral faz coisas diferentes: com a tela desbloqueada, apaga a tela; com a tela apagada, acende; durante uma chamada, encerra. Não é o botão que mudou — é o estado do aparelho que redefine o significado da mesma ação.
Um objeto se comporta de formas diferentes dependendo do seu estado, e cada método vira um `switch (this.estado)` gigante. Adicionar um estado obriga a mexer em todos os métodos, e as transições válidas ficam implícitas e espalhadas — bugs de “pedido entregue foi cancelado” nascem aí.
Crie uma classe por estado, cada uma implementando os métodos do jeito daquele estado. O objeto original (Context) guarda uma referência ao objeto de estado atual e delega tudo a ele. Para transitar, troca-se o objeto de estado. As transições válidas ficam explícitas no código.
switch (pedido.status)repetido em cinco métodos.- Bugs de transição ilegal em produção.
Estrutura
- Context
- Mantém a referência ao estado atual e delega o trabalho a ele.
- State
- Interface com as operações dependentes de estado.
- ConcreteState
- Implementa o comportamento de um estado e dispara as transições permitidas.
Implementação
// ── State: cada operação, uma vez por estado ──────────
class EstadoPedido {
constructor(pedido) { this.pedido = pedido; }
get nome() { return this.constructor.name; }
pagar() { return this.#invalido('pagar'); }
enviar() { return this.#invalido('enviar'); }
entregar() { return this.#invalido('entregar'); }
cancelar() { return this.#invalido('cancelar'); }
#invalido(acao) { throw new Error(`❌ não é possível "${acao}" em ${this.nome}`); }
}
class Aguardando extends EstadoPedido {
pagar() { this.pedido.transitar(new Pago(this.pedido)); return '💳 pagamento aprovado'; }
cancelar() { this.pedido.transitar(new Cancelado(this.pedido)); return '🚫 pedido cancelado'; }
}
class Pago extends EstadoPedido {
enviar() { this.pedido.transitar(new Enviado(this.pedido)); return '📦 despachado'; }
cancelar() { this.pedido.transitar(new Cancelado(this.pedido)); return '🚫 cancelado com estorno'; }
}
class Enviado extends EstadoPedido {
entregar() { this.pedido.transitar(new Entregue(this.pedido)); return '✅ entregue'; }
}
class Entregue extends EstadoPedido {} // estado final: tudo herda "inválido"
class Cancelado extends EstadoPedido {}
// ── Context: fino, apenas delega ──────────────────────
class Pedido {
constructor() { this.estado = new Aguardando(this); this.trilha = ['Aguardando']; }
transitar(novo) { this.estado = novo; this.trilha.push(novo.nome); }
pagar() { return this.estado.pagar(); }
enviar() { return this.estado.enviar(); }
entregar() { return this.estado.entregar(); }
cancelar() { return this.estado.cancelar(); }
}
const p = new Pedido();
console.log(p.pagar()); // 💳 pagamento aprovado
console.log(p.enviar()); // 📦 despachado
console.log(p.entregar()); // ✅ entregue
try { p.cancelar(); } catch (e) { console.log(e.message); }
// ❌ não é possível "cancelar" em Entregue ← a regra é estrutural, não um if esquecido
console.log(p.trilha.join(' → ')); - javax.faces.lifecycle.LifeCycle
- Máquinas de estado de workflow (XState, Spring State Machine)
- Estados de conexão TCP; ciclo de vida de threads e de processos
Consequências
- Cada estado numa classe (Responsabilidade Única).
- Novos estados entram sem alterar os existentes nem o contexto (Aberto/Fechado).
- Elimina condicionais gigantes de máquina de estado.
- Torna as transições válidas explícitas e verificáveis.
- Exagero quando há poucos estados ou eles mudam raramente.
- Mais classes para navegar.
- Um objeto se comporta de forma muito diferente conforme o estado, e os estados mudam com frequência.
- Há condicionais enormes e duplicadas sobre o valor de um campo de estado.
- Existem transições ilegais que precisam ser impedidas.
- São dois estados com pouca lógica — um booleano resolve.
Relações com outros padrões
Verificação
A distinção mais citada entre State e Strategy é:
- Nos estados, os objetos conhecem uns aos outros e comandam a transição; estratégias são independentes e escolhidas pelo cliente
- State usa herança; Strategy usa composição
- Strategy não pode ser trocada em tempo de execução
- State só se aplica a objetos persistidos
A frase de Refactoring Guru resume: “State pode ser visto como uma extensão de Strategy” — com a diferença de que os estados sabem para onde podem ir.