GoF, p. 315 também chamado de Policy
Estratégia
Strategy
Vários algoritmos intercambiáveis para a mesma tarefa, escolhidos em tempo de execução.
Definir uma família de algoritmos, encapsular cada um deles e torná-los intercambiáveis. Strategy permite que o algoritmo varie independentemente dos clientes que o utilizam.
O aplicativo de rotas
Origem e destino são os mesmos; o que muda é como chegar: de carro, a pé, de bicicleta ou de transporte público. Cada modo é um algoritmo completo de cálculo de rota. O app não mudou — você trocou a estratégia com um toque.
Uma classe acumula várias formas de fazer a mesma coisa. Ela cresce a cada nova variação, os conflitos de merge se multiplicam, e mudanças em um algoritmo arriscam quebrar os outros porque tudo vive no mesmo arquivo.
Extraia cada algoritmo para sua própria classe, todas implementando a mesma interface. O Context guarda uma referência a uma estratégia e delega, sem saber qual é. Quem escolhe é o cliente.
if (tipoDeFrete === "sedex") … else if …dentro da regra de negócio.- Uma classe com métodos
calcularA,calcularB,calcularCquase idênticos.
Estrutura
- Strategy
- Interface comum a todos os algoritmos.
- ConcreteStrategy
- Uma implementação específica do algoritmo.
- Context
- Guarda a estratégia atual e delega; expõe um setter para trocá-la.
- Client
- Escolhe a estratégia concreta e injeta no contexto.
Implementação
// ── Estratégias: cada algoritmo isolado e testável ────
class Frete { calcular(peso, distancia) { throw new Error('abstrato'); } }
class Sedex extends Frete { calcular(p, d) { return 12 + p * 2.5 + d * 0.08; } }
class Pac extends Frete { calcular(p, d) { return 6 + p * 1.2 + d * 0.03; } }
class Retirada extends Frete { calcular() { return 0; } }
class FreteGratisAcimaDe extends Frete {
constructor(limite, base) { super(); this.limite = limite; this.base = base; }
calcular(p, d, subtotal) { return subtotal >= this.limite ? 0 : this.base.calcular(p, d); }
}
// ── Context: não sabe COMO se calcula frete ───────────
class Carrinho {
itens = []; peso = 0;
constructor(estrategia) { this.estrategia = estrategia; }
setEstrategia(e) { this.estrategia = e; } // troca a quente
adicionar(nome, preco, peso) { this.itens.push({ nome, preco }); this.peso += peso; }
get subtotal() { return this.itens.reduce((s, i) => s + i.preco, 0); }
get total() { return this.subtotal + this.estrategia.calcular(this.peso, 400, this.subtotal); }
}
const carrinho = new Carrinho(new Pac());
carrinho.adicionar('Livro GoF', 180, 1.2);
carrinho.adicionar('Caneca', 45, 0.4);
console.log(carrinho.total.toFixed(2)); // PAC
carrinho.setEstrategia(new Sedex());
console.log(carrinho.total.toFixed(2)); // SEDEX
carrinho.setEstrategia(new FreteGratisAcimaDe(200, new Sedex()));
console.log(carrinho.total.toFixed(2)); // 225.00 — frete zerado
// 💡 Em JavaScript, uma estratégia costuma ser só uma função:
const estrategias = { sedex: (p, d) => 12 + p * 2.5 + d * 0.08, retirada: () => 0 };
// carrinho.estrategia = { calcular: estrategias.sedex }; - java.util.Comparator — a estratégia de ordenação
- javax.servlet.http.HttpServlet#service
- Estratégias de autenticação do Passport.js; políticas de retry e de cache
Consequências
- Troca algoritmos em tempo de execução.
- Isola implementação de uso; cada algoritmo é testável isoladamente.
- Substitui herança por composição.
- Novos algoritmos entram sem tocar no contexto (Aberto/Fechado).
- O cliente precisa conhecer as diferenças entre as estratégias para escolher.
- Exagero se os algoritmos raramente mudam.
- Em linguagens com funções de primeira classe, classes inteiras podem ser cerimônia desnecessária.
- Você tem muitas variantes de um mesmo algoritmo e quer trocá-las em tempo de execução.
- Há classes parecidas que só diferem no jeito de executar algo.
- Você quer esconder do resto do código estruturas de dados e detalhes de um algoritmo.
- Um `switch` seleciona entre variantes do mesmo comportamento.
- Existem duas variantes estáveis e simples.
Relações com outros padrões
- State
- State é Strategy cujas variantes conhecem umas às outras e trocam a si mesmas.
- Bridge
- Mesma estrutura; Bridge separa dimensões estruturais, Strategy intercambia algoritmos.
- Decorator
- Decorator muda a pele do objeto; Strategy muda as tripas.
- Command
- Command encapsula uma AÇÃO a executar; Strategy, um MODO de fazer algo.
- Template Method
- Template Method varia por HERANÇA (em tempo de compilação, parte do algoritmo); Strategy varia por COMPOSIÇÃO (em tempo de execução, o algoritmo inteiro).
- Flyweight
- Estratégias sem estado podem ser instâncias únicas compartilhadas.
Verificação
Template Method e Strategy resolvem problemas parecidos. A diferença estrutural é:
- Template Method usa herança e varia PARTES do algoritmo; Strategy usa composição e troca o algoritmo INTEIRO em tempo de execução
- Strategy só funciona com algoritmos matemáticos
- Template Method não permite mais de duas subclasses
- Strategy exige que o contexto seja um Singleton
Template Method fixa o esqueleto na superclasse e deixa buracos para as subclasses. Strategy delega tudo a um objeto trocável.