Pular para o conteúdo
GoF Padrões de Projeto Comportamentais
Comportamental escopo de objeto
GoF, p. 305 também chamado de Objects for States

Estado

State

O objeto parece mudar de classe quando seu estado interno muda.

Complexidade média Frequência de uso moderada
Intençãocomo o livro define

Permitir que um objeto altere seu comportamento quando seu estado interno muda. O objeto parecerá ter mudado de classe.

Analogiauma imagem do mundo real

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.

O problema

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í.

A solução

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.

Sintomascomo reconhecer no seu código
  • switch (pedido.status) repetido em cinco métodos.
  • Bugs de transição ilegal em produção.

Estrutura

delegatransitaPedido (Context)- estado+ pagar()+ transitar(s)«interface»EstadoDoPedido+ pagar()+ enviar()+ cancelar()AguardandoPagoEntregue
Fig. 20 — State herda / implementa cria usa / contém
Ver este diagrama sendo desenhado, traço a traço
Participantes
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

Listagem 20 Ciclo de vida de um pedido com transições explícitas
// ── 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(' → '));
Na práticaonde ele já existe
  • 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

A favor
  • 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.
Contra
  • Exagero quando há poucos estados ou eles mudam raramente.
  • Mais classes para navegar.
Use quando
  • 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.
Evite quando
  • São dois estados com pouca lógica — um booleano resolve.

Relações com outros padrões

parecido / fácil de confundir
Strategy
Estrutura idêntica. No State, os estados CONHECEM uns aos outros e disparam transições; nas Strategies, elas se ignoram e quem escolhe é o cliente.
Bridge
Mesma estrutura, intenção diferente.
costuma andar junto
Flyweight
Estados sem campos próprios podem ser compartilhados entre contextos.
Singleton
Estados imutáveis costumam ser instâncias únicas.

Verificação

Questãoa resposta está marcada

A distinção mais citada entre State e Strategy é:

  1. Nos estados, os objetos conhecem uns aos outros e comandam a transição; estratégias são independentes e escolhidas pelo cliente
  2. State usa herança; Strategy usa composição
  3. Strategy não pode ser trocada em tempo de execução
  4. 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.