Menu
Coddy logo textTech

Padrão Strategy

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 95 de 104.

O padrão Strategy define uma família de algoritmos, encapsula cada um deles e os torna intercambiáveis. Isso permite que você altere o comportamento de um objeto em tempo de execução sem modificar seu código - o algoritmo varia independentemente dos clientes que o utilizam.

O padrão consiste em três partes: uma interface Strategy declarando o método do algoritmo, Estratégias Concretas implementando diferentes variações, e um Contexto que utiliza uma estratégia:

#include <iostream>
#include <memory>

// Interface Strategy
class PaymentStrategy {
public:
    virtual void pay(int amount) = 0;
    virtual ~PaymentStrategy() = default;
};

// Estratégias concretas
class CreditCardPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via Credit Card\n";
    }
};

class PayPalPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via PayPal\n";
    }
};

// Contexto
class ShoppingCart {
    std::unique_ptr<PaymentStrategy> strategy;
public:
    void setPaymentMethod(std::unique_ptr<PaymentStrategy> s) {
        strategy = std::move(s);
    }
    
    void checkout(int total) {
        if (strategy) strategy->pay(total);
    }
};

int main() {
    ShoppingCart cart;
    
    cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
    cart.checkout(100);
    
    cart.setPaymentMethod(std::make_unique<PayPalPayment>());
    cart.checkout(50);
}

O ShoppingCart não sabe qual método de pagamento está usando - ele apenas chama pay() em qualquer estratégia que esteja definida. Você pode trocar as estratégias em tempo de execução com setPaymentMethod(), tornando o sistema flexível e fácil de estender com novas opções de pagamento.

Use o Strategy quando você tiver múltiplos algoritmos para uma tarefa específica e quiser alternar entre eles dinamicamente, ou quando quiser evitar instruções condicionais para selecionar o comportamento.

challenge icon

Desafio

Fácil

Vamos construir uma Calculadora de Frete que utiliza o padrão Strategy para calcular os custos de entrega com base em diferentes métodos de envio. Este é um cenário prático onde você precisa trocar algoritmos em tempo de execução — o mesmo pacote pode ser enviado via terrestre, aéreo ou expresso, cada um com sua própria lógica de precificação.

Você organizará seu código em três arquivos:

  • ShippingStrategy.h: Defina sua interface de estratégia e as estratégias de envio concretas.

    Crie uma classe abstrata ShippingStrategy com um método virtual puro calculateCost(double weight) que retorna o custo de envio como um double, junto com um destrutor virtual.

    Em seguida, implemente três estratégias concretas:

    • GroundShipping — custa 1.5 por unidade de peso (weight * 1.5)
    • AirShipping — custa 4.0 por unidade de peso (weight * 4.0)
    • ExpressShipping — custa 6.5 por unidade de peso mais uma taxa fixa de 10.0 (weight * 6.5 + 10.0)
  • ShippingService.h: Crie a classe de contexto que utiliza uma estratégia de envio.

    Sua classe ShippingService deve manter um std::unique_ptr<ShippingStrategy> como um membro privado. Implemente:

    • Um método setStrategy(std::unique_ptr<ShippingStrategy> strategy) para alterar o método de envio
    • Um método calculateShipping(double weight) que utiliza a estratégia atual para calcular e retornar o custo

    Se nenhuma estratégia estiver definida quando calculateShipping for chamado, retorne 0.0.

  • main.cpp: Demonstre a troca de estratégias em tempo de execução.

    Leia duas entradas:

    1. Peso do pacote (double)
    2. Método de envio: ground, air, ou express

    Crie um ShippingService e defina a estratégia apropriada com base no método de entrada. Calcule e imprima o custo de envio.

    Em seguida, mude para uma estratégia diferente (use air se a entrada não for air, caso contrário, use ground) e calcule o custo novamente para o mesmo peso. Isso demonstra o poder de trocar estratégias em tempo de execução.

    Imprima cada custo em sua própria linha com exatamente uma casa decimal, prefixado com o nome do método:

    [Method]: $[cost]

Por exemplo, com as entradas 5.0 e ground:

Ground: $7.5
Air: $20.0

Com as entradas 3.0 e express:

Express: $29.5
Air: $12.0

Com as entradas 10.0 e air:

Air: $40.0
Ground: $15.0

Observe como o ShippingService não precisa conhecer os detalhes de cada algoritmo de precificação — ele simplesmente delega para qualquer estratégia que esteja definida no momento. Você pode facilmente adicionar novos métodos de envio (como entrega por drone ou no mesmo dia) sem modificar a classe de serviço.

Folha de consulta

O padrão Strategy define uma família de algoritmos, encapsula cada um deles e os torna intercambiáveis. Isso permite alterar o comportamento de um objeto em tempo de execução sem modificar seu código.

O padrão consiste em três partes:

  • Interface Strategy: Uma classe abstrata que declara o método do algoritmo
  • Strategies Concretas: Classes que implementam diferentes variações do algoritmo
  • Contexto: Uma classe que usa uma estratégia e pode alternar entre elas
// Interface Strategy
class PaymentStrategy {
public:
    virtual void pay(int amount) = 0;
    virtual ~PaymentStrategy() = default;
};

// Estratégias concretas
class CreditCardPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via Credit Card\n";
    }
};

class PayPalPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via PayPal\n";
    }
};

// Contexto
class ShoppingCart {
    std::unique_ptr<PaymentStrategy> strategy;
public:
    void setPaymentMethod(std::unique_ptr<PaymentStrategy> s) {
        strategy = std::move(s);
    }
    
    void checkout(int total) {
        if (strategy) strategy->pay(total);
    }
};

O contexto não sabe qual estratégia concreta está usando — ele apenas chama o método da interface. As estratégias podem ser trocadas em tempo de execução usando um método setter:

ShoppingCart cart;

cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
cart.checkout(100);

cart.setPaymentMethod(std::make_unique<PayPalPayment>());
cart.checkout(50);

Quando usar: Use o Strategy quando você tiver múltiplos algoritmos para uma tarefa específica e quiser alternar entre eles dinamicamente, ou quando quiser evitar instruções condicionais para selecionar o comportamento.

Experimente você mesmo

#include <iostream>
#include <string>
#include <iomanip>
#include <memory>
#include "ShippingStrategy.h"
#include "ShippingService.h"

int main() {
    double weight;
    std::string method;
    
    std::cin >> weight;
    std::cin >> method;
    
    // Configura a saída para 1 casa decimal
    std::cout << std::fixed << std::setprecision(1);
    
    ShippingService service;
    
    // TODO: Com base no método de entrada ("ground", "air", ou "express"):
    // 1. Define a estratégia apropriada no serviço
    // 2. Calcula e imprime o custo no formato: "[Method]: $[cost]"
    
    // TODO: Alterna para uma estratégia diferente:
    // - Se a entrada foi "air", alterna para GroundShipping
    // - Caso contrário, alterna para AirShipping
    // Calcula e imprime o novo custo
    
    return 0;
}
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