Strategy
Define uma família de algoritmos, encapsula cada um deles, e os torna intercambiáveis. Strategy permite que o algoritmo varie independentemente dos clientes que o usam.
Intenção
Permitir que um objeto selecione um comportamento em tempo de execução encapsulando cada algoritmo atrás de uma interface comum e delegando a execução ao objeto de estratégia atualmente atribuído.
Problema
Você tem uma classe que precisa executar uma tarefa de várias formas diferentes, e se pega usando grandes blocos condicionais (if/else ou switch) para escolher a variação certa. Adicionar uma nova variação exige modificar código existente, violando o Princípio Aberto/Fechado e tornando a classe mais difícil de testar e manter.
Solução
Extraia cada algoritmo para sua própria classe que implementa uma interface Strategy comum. A classe original (Context) mantém uma referência a uma Strategy e delega o trabalho a ela. Os clientes podem trocar estratégias em tempo de execução injetando uma implementação diferente, mantendo a classe Context enxuta e fechada para modificação.
Participantes
- Strategy — declara uma interface comum a todos os algoritmos suportados
- ConcreteStrategy — implementa o algoritmo usando a interface Strategy
- Context — mantém uma referência a um objeto Strategy e delega o trabalho algorítmico a ele
Vantagens
- Elimina declarações condicionais para selecionar comportamento
- Cada algoritmo é isolado em sua própria classe, facilitando testar, estender e trocar
- Segue o Princípio Aberto/Fechado — novas estratégias podem ser adicionadas sem alterar o Context
- Estratégias podem ser compartilhadas entre diferentes contextos se forem stateless
Desvantagens
- Os clientes precisam conhecer as diferentes estratégias e entender quando escolher cada uma
- Aumenta o número de classes no sistema, o que pode parecer superengenharia para casos simples
- Overhead de comunicação entre Context e Strategy se a interface for ampla ou estreita demais
Analogia do mundo real
Considere diferentes rotas até o aeroporto. Você pode dirigir, pegar um ônibus, ir de bicicleta, ou chamar um táxi. Cada uma é uma estratégia diferente para resolver o mesmo problema — chegar ao aeroporto. A escolha depende de orçamento, tempo, e conforto, mas o destino continua o mesmo. Você pode trocar sua estratégia de transporte até o momento de sair de casa.
Casos de uso
- Algoritmos de ordenação — trocar entre quicksort, mergesort, ou heapsort dependendo das características dos dados
- Processamento de pagamento — selecionar entre cartão de crédito, PayPal, transferência bancária, ou cripto no checkout
- Compressão — escolher entre gzip, brotli, ou zstd para diferentes trade-offs de performance/tamanho
- Regras de validação — aplicar diferentes estratégias de validação dependendo do papel do usuário ou tipo de formulário
- Motores de precificação — aplicar estratégias de desconto sazonal, VIP, ou baseadas em cupom
Exemplos de código
interface CompressionStrategy {
String compress(String data);
String name();
}
final class GzipStrategy implements CompressionStrategy {
@Override
public String compress(String data) {
return "[gzip: " + data.length() + " -> " + Math.ceil(data.length() * 0.6) + " bytes]";
}
@Override
public String name() { return "gzip"; }
}
final class BrotliStrategy implements CompressionStrategy {
@Override
public String compress(String data) {
return "[brotli: " + data.length() + " -> " + Math.ceil(data.length() * 0.45) + " bytes]";
}
@Override
public String name() { return "brotli"; }
}
final class NoCompression implements CompressionStrategy {
@Override
public String compress(String data) { return data; }
@Override
public String name() { return "none"; }
}
final class Compressor {
private CompressionStrategy strategy;
Compressor(CompressionStrategy strategy) {
this.strategy = strategy;
}
void setStrategy(CompressionStrategy strategy) {
this.strategy = strategy;
}
String compress(String data) {
return strategy.compress(data);
}
}
public class StrategyDemo {
public static void main(String[] args) {
Compressor compressor = new Compressor(new GzipStrategy());
System.out.println(compressor.compress("some payload"));
compressor.setStrategy(new BrotliStrategy());
System.out.println(compressor.compress("some payload"));
}
}Um contexto Compressor delega a objetos CompressionStrategy intercambiáveis. Novos algoritmos podem ser adicionados sem modificar a classe Compressor.
Padrões relacionados
State
Permite que um objeto altere seu comportamento quando seu estado interno muda. O objeto vai parecer mudar de classe.