Design Patterns: Strategy (Parte 2)

·4 min de leitura

Contexto

No artigo anterior a gente alinhou que design pattern não é regra: é um nome para uma solução recorrente. Aqui vamos focar na intenção do Strategy.

Strategy é um padrão comportamental (GoF). A intenção é: definir uma família de algoritmos, encapsular cada um e torná-los intercambiáveis, para que o comportamento possa variar sem espalhar if/switch pelo código. A descrição é meio confusa mesmo, vou detalhar e ficará claro.

Qual problema o Strategy resolve?

Imagine um serviço que calcula frete. No começo existe só um cálculo. Depois entram: transportadora A, B, regra promocional, frete grátis acima de X.

O caminho “natural” (e perigoso) é ir acumulando condicionais num único método. Isso cresce rápido, fica difícil testar cada regra isoladamente e cada mudança tem o medo de quebrar o resto.

O Strategy ataca isso ao dizer: cada regra de negócio vira um objeto com a mesma “forma” (interface), e quem orquestra só delega para a estratégia atual. Isso evita a explosão de condicionais e torna o código mais fácil de entender e testar.

As peças do Strategy (sem decorar diagrama)

Você pode enxergar Strategy como três papéis. Primeiro existe uma interface (a “forma” do algoritmo), depois existem implementações concretas dessa interface (cada uma com uma regra diferente) e, por fim, existe o contexto: a classe que usa a estratégia e delega para ela. A troca de comportamento acontece compondo o objeto certo, e não editando um método gigante cheio de condicionais.

Exemplo na prática

Abaixo, um exemplo mínimo com readonly, tipagem estática e interface explícita:

<?php

declare(strict_types=1);

interface FreightStrategy
{
    public function quote(float $weightKg, string $zipCode): float;
}

final readonly class StandardFreight implements FreightStrategy
{
    public function quote(float $weightKg, string $zipCode): float
    {
        // Exemplo didático: preço base + peso
        return 10.0 + ($weightKg * 2.5);
    }
}

final readonly class ExpressFreight implements FreightStrategy
{
    public function quote(float $weightKg, string $zipCode): float
    {
        return 25.0 + ($weightKg * 5.0);
    }
}

final class CheckoutService
{
    public function __construct(private FreightStrategy $freight) {}

    public function setFreightStrategy(FreightStrategy $freight): void
    {
        $this->freight = $freight;
    }

    public function totalWithShipping(float $cartTotal, float $weightKg, string $zipCode): float
    {
        return $cartTotal + $this->freight->quote($weightKg, $zipCode);
    }
}

O que está acontecendo aqui é simples: FreightStrategy define o contrato, StandardFreight e ExpressFreight encapsulam regras diferentes e CheckoutService vira o contexto que só delega para quote. O método setFreightStrategy existe para mostrar que dá para trocar em runtime, mas em sistemas mais bem organizados essa composição costuma vir de injeção de dependência (ou fábrica) no bootstrap, e não via setter.

Strategy vs “só extrair funções”

Extrair funções ajuda, mas Strategy brilha quando:

  • você tem várias variações do mesmo “tipo de operação”;
  • quer testar cada variação isoladamente;
  • quer adicionar novas variações sem mexer no núcleo do contexto (alinhado com Open/Closed, tema que vamos ver na série de SOLID).

Quando NÃO forçar Strategy

Se existe uma regra simples e sem perspectiva de crescimento, Strategy pode ser overengineering.

Também desconfie se a “estratégia” virar um objeto que só existe para esconder um if único — às vezes um match pequeno e claro é mais honesto.

Testes ficam mais fáceis

Com Strategy, você testa o contexto injetando doubles (fake/stub) da interface, e testa cada estratégia sem passar pelo CheckoutService.

Conclusão

Strategy é sobre intercambiar algoritmos via composição. Ele reduz acoplamento “condicional” e melhora coesão: cada classe faz uma coisa.

No próximo artigo da série, falamos de Simple Factory: uma forma comum (não-GoF) de centralizar criação quando você tem várias estratégias/classes parecidas.

Referências

  • Design Patterns: Elements of Reusable Object-Oriented Software (GoF).
  • Refactoring Guru – Strategy: https://refactoring.guru/design-patterns/strategy

Links