3.10 Strategy
The Strategy pattern encapsulates a family of interchangeable algorithms as objects so they can be swapped at runtime. The client holds a strategy interface; which algorithm runs depends on the injected strategy — while the call stays the same.
Navigation software is a perfect example: for the same "A to B," you can take the fastest, shortest, or scenic route. The lab below lets you switch routing strategies and see how one origin/destination yields different time/distance tradeoffs.
Replace inheritance with composition
interface RouteStrategy { Route build(Point a, Point b); }
class Navigator {
private RouteStrategy strategy; // holds a strategy
void setStrategy(RouteStrategy s) { this.strategy = s; }
Route navigate(Point a, Point b) {
return strategy.build(a, b); // delegate to the current strategy
}
}A new algorithm = a new strategy class, with no change to Navigator — exactly the Open/Closed Principle. The payment lab below lets you switch between credit card / PayPal / crypto and watch pay() run an entirely different flow.
Strategy vs Template Method
Both make "part of an algorithm" vary, but by opposite means:
- Strategy uses composition: make the whole algorithm an injectable object, swapped wholesale at runtime.
- Template Method uses inheritance: the base class fixes the skeleton and subclasses override individual steps.
Strategy is more flexible (runtime-swappable, composable); Template Method is more economical (shared skeleton, reused code).
Relationship to first-class functions
In languages with first-class functions/lambdas, a simple strategy often degrades into "pass a function." list.sort(comparator) or passing a callback are lightweight forms of Strategy. Make strategy classes when rules are complex or need state or multiple methods; pass a lambda for a one-off pure function.