Menu
Coddy logo textTech

Padrão Strategy

Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 74 de 91.

O Padrão Strategy é um padrão de projeto comportamental que permite definir uma família de algoritmos, encapsular cada um em sua própria classe e torná-los intercambiáveis em tempo de execução. O objeto que utiliza a estratégia não precisa saber qual algoritmo está executando.

Imagine um carrinho de compras que pode aplicar diferentes estratégias de desconto. Em vez de codificar rigidamente a lógica de desconto com condicionais, você define cada desconto como uma classe separada:

<?php
interface DiscountStrategy {
    public function calculate(float $total): float;
}

class NoDiscount implements DiscountStrategy {
    public function calculate(float $total): float {
        return $total;
    }
}

class PercentageDiscount implements DiscountStrategy {
    public function __construct(private int $percent) {}
    
    public function calculate(float $total): float {
        return $total - ($total * $this->percent / 100);
    }
}

class FixedDiscount implements DiscountStrategy {
    public function __construct(private float $amount) {}
    
    public function calculate(float $total): float {
        return max(0, $total - $this->amount);
    }
}

A classe de contexto aceita qualquer estratégia e delega o cálculo:

<?php
class ShoppingCart {
    public function __construct(private DiscountStrategy $discount) {}
    
    public function setDiscount(DiscountStrategy $discount): void {
        $this->discount = $discount;
    }
    
    public function checkout(float $total): float {
        return $this->discount->calculate($total);
    }
}

$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100) . "\n";

$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100);

Saída:

100
80

O Padrão Strategy brilha quando você tem várias maneiras de realizar uma operação e deseja alternar entre elas sem modificar a classe que as utiliza. Adicionar um novo tipo de desconto significa criar uma nova classe, sem tocar no código existente.

challenge icon

Desafio

Fácil

Vamos construir uma calculadora de custos de envio usando o Strategy Pattern. Diferentes métodos de envio têm diferentes regras de precificação — o envio padrão cobra uma taxa fixa, o envio expresso adiciona uma porcentagem e o envio noturno combina ambos. O Strategy Pattern permite que você troque esses cálculos sem alterar o código que os utiliza.

Você organizará seu código em quatro arquivos:

  • ShippingStrategy.php — Crie uma interface ShippingStrategy que define o contrato para todos os métodos de envio. Ela deve ter um método calculate(float $weight): float que recebe o peso do pacote e retorna o custo de envio.
  • ShippingMethods.php — Inclua o arquivo ShippingStrategy e crie três classes que implementam a interface:
    • StandardShipping — Cobra uma taxa fixa de $5.00 independentemente do peso
    • ExpressShipping — Cobra $2.00 por quilograma (multiplique o peso por 2)
    • OvernightShipping — Cobra uma taxa base de $10.00 mais $3.00 por quilograma
  • ShippingCalculator.php — Inclua o arquivo ShippingStrategy e crie uma classe ShippingCalculator que atua como o contexto. Sua calculadora deve:
    • Aceitar uma ShippingStrategy em seu construtor (use promoção de construtor)
    • Ter um método setStrategy(ShippingStrategy $strategy): void para alterar o método de envio em tempo de execução
    • Ter um método calculateCost(float $weight): float que delega para a estratégia atual
  • main.php — Inclua os arquivos ShippingMethods e ShippingCalculator. Você receberá duas entradas: um tipo de envio ("standard", "express", ou "overnight") e o peso de um pacote.

    Crie um ShippingCalculator começando com StandardShipping. Com base na entrada do tipo de envio, use setStrategy() para mudar para o método de envio apropriado. Em seguida, calcule o custo para o peso fornecido e exiba o resultado formatado com duas casas decimais.

    A saída deve ser: Shipping cost: $[cost]

Este desafio demonstra como o Strategy Pattern torna seu sistema de envio flexível — adicionar um novo método de envio como SameDayShipping requer apenas a criação de uma nova classe que implementa a interface, sem tocar na calculadora ou nos métodos de envio existentes.

Folha de consulta

O Strategy Pattern (Padrão de Estratégia) é um padrão de design comportamental que define uma família de algoritmos, encapsula cada um em sua própria classe e os torna intercambiáveis em tempo de execução.

Defina uma interface para a estratégia:

<?php
interface DiscountStrategy {
    public function calculate(float $total): float;
}

Implemente estratégias concretas:

<?php
class NoDiscount implements DiscountStrategy {
    public function calculate(float $total): float {
        return $total;
    }
}

class PercentageDiscount implements DiscountStrategy {
    public function __construct(private int $percent) {}
    
    public function calculate(float $total): float {
        return $total - ($total * $this->percent / 100);
    }
}

class FixedDiscount implements DiscountStrategy {
    public function __construct(private float $amount) {}
    
    public function calculate(float $total): float {
        return max(0, $total - $this->amount);
    }
}

Crie uma classe de contexto que utiliza a estratégia:

<?php
class ShoppingCart {
    public function __construct(private DiscountStrategy $discount) {}
    
    public function setDiscount(DiscountStrategy $discount): void {
        $this->discount = $discount;
    }
    
    public function checkout(float $total): float {
        return $this->discount->calculate($total);
    }
}

Use o padrão injetando estratégias:

<?php
$cart = new ShoppingCart(new NoDiscount());
echo $cart->checkout(100); // 100

$cart->setDiscount(new PercentageDiscount(20));
echo $cart->checkout(100); // 80

O Strategy Pattern permite que você alterne entre algoritmos sem modificar a classe que os utiliza. Adicionar novas estratégias requer apenas a criação de novas classes que implementem a interface.

Experimente você mesmo

<?php
require_once 'ShippingMethods.php';
require_once 'ShippingCalculator.php';

// Ler entrada
$shippingType = trim(fgets(STDIN));
$weight = floatval(fgets(STDIN));

// TODO: Criar um ShippingCalculator começando com StandardShipping

// TODO: Com base na entrada do tipo de envio ("standard", "express" ou "overnight"),
// use setStrategy() para mudar para o método de envio apropriado

// TODO: Calcular o custo para o peso fornecido

// TODO: Imprimir o resultado formatado com duas casas decimais
// Formato de saída: Shipping cost: $[cost]

?>
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos