Strategy
Put each interchangeable algorithm behind one interface, and let the caller pick which one to plug in.
Big idea
Strategy pulls a family of interchangeable algorithms out of a class and puts each behind the same interface. The class (the context) holds one strategy and delegates to it; callers choose — or swap — the strategy at runtime. New algorithms are new classes, not new branches.
The problem it solves
A ride-hailing app prices trips. Standard fare, surge pricing during peaks, shared rides split between passengers, a flat airport fare. In one price() method that's a growing if mode == … ladder, each branch with its own maths, all in the same class, all re-tested every time a promotion team adds a new mode. The maths for surge can't be reused in the delivery app without dragging in the rest.
Think of it like
Getting to the airport. Taxi, train, or cycling — same goal, different trade-offs in cost and time. You pick based on today's situation (luggage, weather, budget), and the trip's purpose doesn't change. The method of travel is a plug-in to "get to the airport".
Structure
In code
class Standard:
def price(self, trip):
return 2.50 + 1.20 * trip.km + 0.25 * trip.minutes
class Surge:
def __init__(self, multiplier):
self.multiplier = multiplier
self.base = Standard() # strategies can reuse each other
def price(self, trip):
return self.base.price(trip) * self.multiplier
class Shared:
def __init__(self, riders):
self.riders = riders
def price(self, trip):
return Standard().price(trip) * 1.3 / self.riders
class AirportFlat:
def price(self, trip):
return 35.0
class Trip: # the context
def __init__(self, km, minutes, pricing):
self.km, self.minutes, self.pricing = km, minutes, pricing
def fare(self):
return round(self.pricing.price(self), 2)
for label, pricing in [("standard", Standard()), ("surge x1.8", Surge(1.8)),
("shared x3", Shared(3)), ("airport", AirportFlat())]:
print(f"{label:11} {Trip(18, 30, pricing).fare():6.2f}")
trip = Trip(18, 30, Standard())
trip.pricing = Surge(1.4) # swapped at runtime when demand spikes
print(f"{'re-priced':11} {trip.fare():6.2f}")output
standard 31.60
surge x1.8 56.88
shared x3 13.69
airport 35.00
re-priced 44.24Run and edit it in the app.
In Python, when a strategy is a single method with no state, you can skip the classes and pass a function — Trip(18, 30, price=lambda t: 10 + t.km) — with the context calling self.price(self).
In the Strategy pattern, who usually chooses the strategy?
Watch it
Hands on
The shipping calculator below picks its rule with a type flag, and a new "express" option was bolted onto one branch incorrectly — express orders over 50 kg are charged as standard. Restructure it into strategies so each rule is self-contained and the output matches.
def shipping_cost(kind, kg):
if kind == "standard" or kg > 50:
return 5 + 0.5 * kg
elif kind == "express":
return 12 + 1.1 * kg
elif kind == "pickup":
return 0.0
for kind, kg in [("standard", 10), ("express", 10), ("express", 60), ("pickup", 5)]:
print(f"{kind:8} {kg:3} kg -> {shipping_cost(kind, kg):6.2f}")output
standard 10 kg -> 10.00
express 10 kg -> 23.00
express 60 kg -> 78.00
pickup 5 kg -> 0.00Run and edit it in the app.
When to use it — and when not to
Use it when several variants of an algorithm exist and the choice depends on runtime conditions or configuration: pricing, sorting and ranking, compression, validation rules, retry/back-off policies, route planning. Skip it for two stable variants that never grow — an if is honest — and in Python don't build classes where a plain function argument does the job.
Pros and cons
| Pros | Cons |
|---|---|
| Swap algorithms at runtime | Clients must know the strategies to choose one |
| Each algorithm isolated and testable | More classes for simple variations |
| New variants without touching the context (Open/Closed) | Strategies may need data the context must expose |
How it relates
- State has the same structure; states switch themselves, strategies are chosen from outside.
- Template Method varies steps through inheritance; Strategy varies the whole algorithm through composition.
- Decorator changes an object's outside; Strategy changes its insides.
- Bridge is the structural cousin: it splits a whole hierarchy, not just one algorithm.
- Replace Conditional with Polymorphism (Simplifying Conditionals) is how you get here from a ladder.
How does Strategy differ from Template Method?
Key terms
- Strategy — An interchangeable algorithm behind a shared interface.
- Context — The object that holds a strategy and delegates the varying work to it.
- Higher-order function — A function that takes another function as an argument — Python's lightweight strategy.