Card 01/ 09
Strategy Pattern: Swapping an Algorithm Without Changing Its Caller
A shipping cost calculator that grows an if for every carrier is the problem a strategy solves.
The Strategy pattern puts each version of an algorithm in its own class behind a shared interface, so the code that uses the algorithm can be handed any version and never needs to know which. A new version is then a new class, and the code that uses it is not edited. This card covers the problem that calls for it, the intent, and when it fits.
The problem is a conditional that chooses between algorithms. A checkout works out shipping by carrier: standard, express, or a collection point. Written as one function, it grows a branch for every carrier, and every new carrier means editing the function that every order passes through.
Without the patternthe checkout chooses
def shipping_pence(carrier, weight_kg):
if carrier == "standard":
return 399 + 100 * weight_kg
elif carrier == "express":
return 899 + 200 * weight_kg
elif carrier == "collection":
return 199Each new carrier adds a branch to this function, and a typo in a carrier name returns None.
With the patternthe caller chooses
class Express(Shipping):
def cost_pence(self, weight_kg):
return 899 + 200 * weight_kg
Checkout(Express()).total_pence(4200, 3)Each carrier is one class, and the checkout calls cost_pence on whichever it was given.
The Gang of Four state the intent as: define a family of algorithms, encapsulate each one, and make them interchangeable, so the algorithm can vary independently of the clients that use it.
A route planner is a familiar picture. Driving, walking and cycling are three ways to compute a route, and the map, the start and the destination stay the same whichever is chosen. The picture breaks on who chooses: a map app usually picks a mode for you, while a strategy is chosen by the caller and handed in.
Strategy is one of eleven behavioural patterns, the family concerned with which object does which part of the work, and how objects communicate.
| Pattern | What it does |
|---|---|
| Chain of Responsibility | Passes a request along a chain of handlers until one handles it. |
| Command | Turns a request into an object that can be stored, queued or undone. |
| Interpreter | Represents a small language's grammar as classes and evaluates its sentences. |
| Iterator | Visits the elements of a collection without exposing how it is stored. |
| Mediator | Routes the communication between objects through one central object. |
| Memento | Captures an object's state so it can be restored later. |
| Observer | Notifies many objects when one object changes. |
| State | Changes an object's behaviour when its internal state changes. |
| Strategy (this topic) | Swaps one algorithm for another behind a common interface. |
| Template Method | Fixes an algorithm's steps in a base class and lets subclasses fill some in. |
| Visitor | Adds operations to a class hierarchy without changing its classes. |
Use it when
- One job has several interchangeable ways of being done, and the choice is made by the caller or by configuration.
- A conditional that picks between algorithms keeps growing a branch.
- Each algorithm should be testable on its own, without the code that uses it.
Avoid it when
- There are two ways and there will never be a third, so an if statement is clearer.
- The algorithms need different inputs, so no single interface fits them all.
- The choice changes as the object's own state changes, which is the State pattern's job.
← → move through sections and cards