Pular para o conteúdo
Comportamentais

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

DocumentContext.java
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.