Descobrir o State Pattern foi um ponto de virada. Esse padrão não é o mais falado, mas resolve um problema real: código inchado por condicionais monstruosas.
Sabe aquelas máquinas de estado cheias de if e switch? O código cresce, novos estados surgem e, de repente, qualquer alteração vira uma dor de cabeça. Já viu algo assim?
class User {
    constructor(name) {
        this.name = name;
        this.status = "pending"; // Estado inicial
    }
    changeStatus(newStatus) {
        switch (newStatus) {
            case "active":
                if (this.status === "active") {
                    console.log("Já está ativo...");
                } else {
                    this.status = "active";
                    console.log("Usuário ativado com sucesso.");
                }
                break;
            case "blocked":
                if (this.status === "blocked") {
                    console.log("Usuário já está bloqueado.");
                } else {
                    this.status = "blocked";
                    console.log("Usuário bloqueado com sucesso.");
                }
                break;
            case "canceled":
                if (this.status === "canceled") {
                    console.log("Usuário já está cancelado.");
                } else {
                    this.status = "canceled";
                    console.log("Usuário cancelado com sucesso.");
                }
                break;
            default:
                console.log("Status inválido.");
        }
    }
    getStatus() {
        console.log(`Status atual: ${this.status}`);
    }
}
No começo, parece tranquilo. Mas conforme o sistema evolui, o inferno começa: um mar de condicionais espalhadas pelo código. Manutenção? Um pesadelo.
Solução
O State Pattern resolve isso. Em vez de espalhar lógica de estado, cada estado vira uma classe separada. O objeto principal apenas mantém uma referência ao estado atual e delega as decisões para ele.
Menos bagunça, mais organização. E quando precisar adicionar um novo estado? Só criar uma nova classe, sem tocar no código existente. Quer código flexível e fácil de manter? State Pattern é o caminho.
Exemplo
Aqui está um exemplo de implementação do State Pattern em TypeScript para modelar o status de uma conta (Account), que pode estar em um dos seguintes estados:
ActiveWaitingActivationBlockedCancelled
Cada estado implementará um comportamento diferente para a conta. Vamos definir as classes de estado e a classe de contexto (Account), que gerencia o estado atual.
📌 Passo 1: Definir a Interface do Estado
Cada estado da conta precisa implementar essa interface para garantir que todos os estados tenham os mesmos métodos.
interface AccountState {
  activate(): void;
  block(): void;
  cancel(): void;
  getStatus(): string;
}
📌 Passo 2: Criar as Implementações dos Estados
Cada estado terá uma implementação específica, alterando o comportamento da conta de acordo com a regra de negócio.
class ActiveState implements AccountState {
  constructor(private account: Account) {}
  activate(): void {
    console.log("A conta já está ativa.");
  }
  block(): void {
    console.log("Bloqueando a conta...");
    this.account.setState(new BlockedState(this.account));
  }
  cancel(): void {
    console.log("Cancelando a conta...");
    this.account.setState(new CancelledState(this.account));
  }
  getStatus(): string {
    return "Active";
  }
}
class WaitingActivationState implements AccountState {
  constructor(private account: Account) {}
  activate(): void {
    console.log("Ativando a conta...");
    this.account.setState(new ActiveState(this.account));
  }
  block(): void {
    console.log("A conta ainda não está ativada e não pode ser bloqueada.");
  }
  cancel(): void {
    console.log("Cancelando a conta antes da ativação...");
    this.account.setState(new CancelledState(this.account));
  }
  getStatus(): string {
    return "WaitingActivation";
  }
}
class BlockedState implements AccountState {
  constructor(private account: Account) {}
  activate(): void {
    console.log("Não é possível ativar uma conta bloqueada. Contate o suporte.");
  }
  block(): void {
    console.log("A conta já está bloqueada.");
  }
  cancel(): void {
    console.log("Cancelando a conta bloqueada...");
    this.account.setState(new CancelledState(this.account));
  }
  getStatus(): string {
    return "Blocked";
  }
}
class CancelledState implements AccountState {
  constructor(private account: Account) {}
  activate(): void {
    console.log("Uma conta cancelada não pode ser reativada.");
  }
  block(): void {
    console.log("Uma conta cancelada não pode ser bloqueada.");
  }
  cancel(): void {
    console.log("A conta já está cancelada.");
  }
  getStatus(): string {
    return "Cancelled";
  }
}
  
  
  📌 Passo 3: Criar a Classe Account
Essa classe é sua entidade e gerencia o estado atual da conta e delega chamadas para o estado correto.
class Account {
  private state: AccountState;
  constructor() {
    // Estado inicial: aguardando ativação
    this.state = new WaitingActivationState(this);
  }
  setState(state: AccountState): void {
    this.state = state;
  }
  activate(): void {
    this.state.activate();
  }
  block(): void {
    this.state.block();
  }
  cancel(): void {
    this.state.cancel();
  }
  getStatus(): string {
    return this.state.getStatus();
  }
}
📌 Passo 4: Testar a Implementação
Agora, vamos criar uma conta e testar as transições de estado, a execução seria algo assim:
const myAccount = new Account();
console.log("Status inicial:", myAccount.getStatus());
myAccount.activate(); // Deve ativar a conta
console.log("Status atual:", myAccount.getStatus());
myAccount.block(); // Deve bloquear a conta
console.log("Status atual:", myAccount.getStatus());
myAccount.cancel(); // Deve cancelar a conta
console.log("Status atual:", myAccount.getStatus());
myAccount.activate(); // Tentativa de ativar uma conta cancelada (deve falhar)
console.log("Status final:", myAccount.getStatus());
📌 Explicação
- Encapsulamento dos estados: Cada estado implementa um comportamento específico e sabe para qual estado pode transitar.
 - 
Mudança dinâmica de comportamento: A classe 
Accountdelega a execução das ações ao estado atual. - 
Evita 
if-elsedesnecessários: O código fica mais organizado e escalável. 
Isso garante uma implementação flexível e permite adicionar novos estados sem modificar a lógica central da Account. 🚀
Conclusão
No State Pattern que a gente montou, vários princípios entraram em jogo sem a gente nem precisar pensar muito. Olha só:
Encapsulamento? Sim Cada estado tem sua própria lógica, sem espalhar código bagunçado por aí. O Account nem precisa saber como cada estado funciona, só delega e segue o jogo.
Responsabilidade Única? Sim Cada classe faz uma coisa só. O estado lida com as regras do status da conta, e o Account só troca de estado quando precisa. Sem mistura de responsabilidades.
Aberto/Fechado? Sim. Se amanhã precisar de um novo estado tipo SuspendedState, só criar e encaixar. Não precisa mexer no código velho e correr risco de quebrar tudo.
Substituição de Liskov? Sim. O Account nem sabe qual estado tá rodando. Ele só chama os métodos da interface e confia que o estado resolve. Pode trocar um pelo outro sem dor de cabeça.
Inversão de Dependência? Sim. O Account depende da abstração (AccountState), não de implementações específicas. Zero acoplamento, total flexibilidade.
No fim, isso aqui é código escalável, fácil de manter e sem aquele monte de if espalhado. Quer mudar um comportamento? Troca o estado e pronto. Isso é POO do jeito certo. 🚀
Links
https://refactoring.guru/pt-br/design-patterns/state
https://elemarjr.com/clube-de-estudos/artigos/voce-precisa-conhecer-o-state-pattern/
    
Top comments (0)