Software Architecture Lab
Voltar para conceitos
PatternsIntermediário14 min

Strategy

Substitua condicionais que crescem com o negócio por comportamentos intercambiáveis. O Strategy isola o que varia e deixa o restante do código estável.

Resumo

Strategy é um padrão comportamental que define uma família de algoritmos, encapsula cada um deles e os torna intercambiáveis. O código que usa o algoritmo não precisa saber qual variante está executando.

Em termos práticos: quando você tem lógica que muda dependendo de um tipo, contexto ou configuração, Strategy é a forma de isolar essa variação sem poluir o resto do código com condicionais.

Problema que resolve

Blocos if/else ou switch que crescem a cada nova regra de negócio são o sintoma clássico. Cada adição exige abrir a mesma função, entender tudo de novo e torcer para não quebrar os casos anteriores.

// Este bloco vai crescer para sempre:
function aplicarDesconto(pedido: Pedido) {
  if (pedido.cliente === "vip") {
    return pedido.total * 0.8;
  } else if (pedido.cliente === "recorrente") {
    return pedido.total * 0.9;
  } else if (pedido.cliente === "novo") {
    return pedido.total * 0.95;
  }
  return pedido.total;
}

Antes e depois

Condicional que cresce com o negócio
function aplicarDesconto(pedido: Pedido) {
  if (pedido.cliente === "vip") {
    return pedido.total * 0.8;
  } else if (pedido.cliente === "recorrente") {
    return pedido.total * 0.9;
  } else if (pedido.cliente === "novo") {
    return pedido.total * 0.95;
  }
  return pedido.total;
}
// cada nova categoria de cliente = nova linha aqui
Strategy — cada regra isolada
interface DescontoStrategy {
  aplicar(total: number): number;
}
 
const descontos: Record<string, DescontoStrategy> = {
  vip:        { aplicar: (v) => v * 0.8 },
  recorrente: { aplicar: (v) => v * 0.9 },
  novo:       { aplicar: (v) => v * 0.95 },
  padrao:     { aplicar: (v) => v },
};
 
function aplicarDesconto(pedido: Pedido) {
  const estrategia = descontos[pedido.cliente] ?? descontos.padrao;
  return estrategia.aplicar(pedido.total);
}
// nova categoria = nova entrada no objeto, zero mudança na função

Quando usar

  • A mesma operação tem variações que mudam por tipo, contexto ou configuração.
  • Um if/else ou switch cresce a cada sprint com novos casos.
  • Você precisa trocar o comportamento em tempo de execução (ex: tema, idioma, modo de pagamento).
  • Testes unitários estão complicados porque uma função faz coisas demais.

Quando não usar

  • Você tem dois casos e a lógica raramente muda. Um if simples é mais legível.
  • A "família de algoritmos" tem só um membro hoje e sem previsão de crescer.

Onde aparece no dia a dia

  • Validação de formulários: cada campo tem sua strategy de validação.
  • Cálculo de frete: cada transportadora implementa a mesma interface.
  • Autenticação: JWT, OAuth, session — cada um é uma strategy.
  • Renderização condicional por permissão: cada role renderiza uma versão diferente.
  • Ordenação de listas: critérios de ordenação como strategies intercambiáveis.

Erros comuns

  • Criar uma interface Strategy com um único método execute() genérico demais — o nome importa, seja específico.
  • Usar Strategy quando um simples mapa de funções resolveria sem a cerimônia de classes e interfaces.
  • Injetar a strategy via construtor quando ela deveria ser passada por parâmetro (mais flexível).

Conceitos relacionados

Conteúdos relacionados