Strategy pattern: swap behaviour

Put interchangeable algorithms behind one interface. A context delegates to the selected strategy instead of branching through a growing switch.

Time O(1) per calculationSpace one strategy objectCode C++, Python and C#
Watch it animatedStep through it, with the running line highlighted. Free with an account.

The idea

Checkout is the context. It delegates the calculation to the IDiscountStrategy object it receives. Switching to NoDiscount changes the behaviour without adding a conditional to Checkout.

Code

class DiscountStrategy {
public:
    virtual double apply(double subtotal) = 0;
    virtual ~DiscountStrategy() = default;
};
class NoDiscount : public DiscountStrategy {
public:
    double apply(double subtotal) override { return subtotal; }
};
class TenPercentOff : public DiscountStrategy {
public:
    double apply(double subtotal) override { return subtotal * 0.9; }
};
class Checkout {
    DiscountStrategy& discount;
public:
    Checkout(DiscountStrategy& discount) : discount(discount) {}
    double total(double subtotal) { return discount.apply(subtotal); }
};

int main() {
    TenPercentOff ten;
    Checkout checkout(ten);
    cout << checkout.total(100) << "\n"; // 90
}
from typing import Protocol

class DiscountStrategy(Protocol):
    def apply(self, subtotal: float) -> float: ...

class NoDiscount:
    def apply(self, subtotal: float) -> float: return subtotal

class TenPercentOff:
    def apply(self, subtotal: float) -> float: return subtotal * 0.9

class Checkout:
    def __init__(self, discount: DiscountStrategy):
        self.discount = discount
    def total(self, subtotal: float) -> float:
        return self.discount.apply(subtotal)

checkout = Checkout(TenPercentOff())
print(checkout.total(100))  # 90.0
interface IDiscountStrategy {
    decimal Apply(decimal subtotal);
}
class NoDiscount : IDiscountStrategy {
    public decimal Apply(decimal subtotal) => subtotal;
}
class TenPercentOff : IDiscountStrategy {
    public decimal Apply(decimal subtotal) => subtotal * 0.9m;
}
class Checkout {
    private readonly IDiscountStrategy discount;
    public Checkout(IDiscountStrategy discount) { this.discount = discount; }
    public decimal Total(decimal subtotal) => discount.Apply(subtotal);
}

var checkout = new Checkout(new TenPercentOff());
Console.WriteLine(checkout.Total(100m)); // 90

Tips

In the .NET library

Common examples include payment methods, shipping-cost rules, compression formats, and validation policies. Dependency injection can select a default strategy; a factory can select one from runtime input.