Menu
Coddy logo textTech

Strategy Pattern

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 95 of 104.

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This lets you change an object's behavior at runtime without modifying its code - the algorithm varies independently from the clients that use it.

The pattern consists of three parts: a Strategy interface declaring the algorithm method, Concrete Strategies implementing different variations, and a Context that uses a strategy:

#include <iostream>
#include <memory>

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

// Concrete strategies
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";
    }
};

// Context
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);
}

The ShoppingCart doesn't know which payment method it's using - it just calls pay() on whatever strategy is set. You can swap strategies at runtime with setPaymentMethod(), making the system flexible and easy to extend with new payment options.

Use Strategy when you have multiple algorithms for a specific task and want to switch between them dynamically, or when you want to avoid conditional statements for selecting behavior.

challenge icon

Challenge

Easy

Let's build a Shipping Calculator that uses the Strategy pattern to calculate delivery costs based on different shipping methods. This is a practical scenario where you need to swap algorithms at runtime—the same package might be shipped via ground, air, or express, each with its own pricing logic.

You'll organize your code across three files:

  • ShippingStrategy.h: Define your strategy interface and concrete shipping strategies.

    Create an abstract ShippingStrategy class with a pure virtual method calculateCost(double weight) that returns the shipping cost as a double, along with a virtual destructor.

    Then implement three concrete strategies:

    • GroundShipping — costs 1.5 per unit of weight (weight * 1.5)
    • AirShipping — costs 4.0 per unit of weight (weight * 4.0)
    • ExpressShipping — costs 6.5 per unit of weight plus a flat fee of 10.0 (weight * 6.5 + 10.0)
  • ShippingService.h: Create the context class that uses a shipping strategy.

    Your ShippingService class should hold a std::unique_ptr<ShippingStrategy> as a private member. Implement:

    • A setStrategy(std::unique_ptr<ShippingStrategy> strategy) method to change the shipping method
    • A calculateShipping(double weight) method that uses the current strategy to compute and return the cost

    If no strategy is set when calculateShipping is called, return 0.0.

  • main.cpp: Demonstrate switching strategies at runtime.

    Read two inputs:

    1. Package weight (double)
    2. Shipping method: ground, air, or express

    Create a ShippingService and set the appropriate strategy based on the input method. Calculate and print the shipping cost.

    Then switch to a different strategy (use air if the input wasn't air, otherwise use ground) and calculate the cost again for the same weight. This demonstrates the power of swapping strategies at runtime.

    Print each cost on its own line with exactly one decimal place, prefixed with the method name:

    [Method]: $[cost]

For example, with inputs 5.0 and ground:

Ground: $7.5
Air: $20.0

With inputs 3.0 and express:

Express: $29.5
Air: $12.0

With inputs 10.0 and air:

Air: $40.0
Ground: $15.0

Notice how the ShippingService doesn't need to know the details of each pricing algorithm—it simply delegates to whatever strategy is currently set. You can easily add new shipping methods (like drone delivery or same-day) without modifying the service class at all.

Cheat sheet

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This allows changing an object's behavior at runtime without modifying its code.

The pattern consists of three parts:

  • Strategy interface: An abstract class declaring the algorithm method
  • Concrete Strategies: Classes implementing different algorithm variations
  • Context: A class that uses a strategy and can switch between them
// Strategy interface
class PaymentStrategy {
public:
    virtual void pay(int amount) = 0;
    virtual ~PaymentStrategy() = default;
};

// Concrete strategies
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";
    }
};

// Context
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);
    }
};

The context doesn't know which concrete strategy it's using—it just calls the interface method. Strategies can be swapped at runtime using a setter method:

ShoppingCart cart;

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

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

When to use: Use Strategy when you have multiple algorithms for a specific task and want to switch between them dynamically, or when you want to avoid conditional statements for selecting behavior.

Try it yourself

#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;
    
    // Set output to 1 decimal place
    std::cout << std::fixed << std::setprecision(1);
    
    ShippingService service;
    
    // TODO: Based on the input method ("ground", "air", or "express"):
    // 1. Set the appropriate strategy on the service
    // 2. Calculate and print the cost in format: "[Method]: $[cost]"
    
    // TODO: Switch to a different strategy:
    // - If input was "air", switch to GroundShipping
    // - Otherwise, switch to AirShipping
    // Calculate and print the new cost
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming