Pular para o conteúdo
Comportamentais

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

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