Low-level design2 min
Strategy Pattern
Type: Behavioral Pattern
Relevance: Extremely High. Arguably the most frequently used design pattern in modern OOP.
The Strategy pattern lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable at runtime.
Real-Life Analogy
Think of Google Maps Routing. You want to get to the airport. Google Maps has a routing feature.
- You can route via Car (Algorithm 1).
- You can route via Walking (Algorithm 2).
- You can route via Public Transit (Algorithm 3).
The overarching goal is the same (find a route), but the specific mathematical algorithm used to find the route changes drastically. You (the client) select the Strategy, and the Navigator executes it.
The Problem
If you have a class that does something specific in a lot of different ways, you end up with a massive class filled with if (strategy == "CAR") { ... } else if (strategy == "WALKING") { ... }.
Adding a new algorithm (e.g., "Bicycle") requires modifying the core class, violating the Open/Closed Principle.
How to Implement Strategy
Extract all the algorithms into their own classes implementing a common Strategy interface. The original class (Context) maintains a reference to a Strategy object and delegates the work to it.
Example in Code
// 1. The Strategy Interface
interface RouteStrategy {
buildRoute(A: string, B: string): void;
}
// 2. Concrete Strategies
class RoadStrategy implements RouteStrategy {
buildRoute(A: string, B: string): void {
console.log(`Calculating ROAD route from ${A} to ${B}... (Takes 30 mins)`);
}
}
class WalkingStrategy implements RouteStrategy {
buildRoute(A: string, B: string): void {
console.log(`Calculating WALKING route from ${A} to ${B}... (Takes 3 hours)`);
}
}
class PublicTransitStrategy implements RouteStrategy {
buildRoute(A: string, B: string): void {
console.log(`Calculating TRANSIT route from ${A} to ${B}... (Takes 45 mins)`);
}
}
// 3. The Context
class Navigator {
private strategy: RouteStrategy;
constructor(strategy: RouteStrategy) {
this.strategy = strategy;
}
// Allows swapping the strategy at runtime!
public setStrategy(strategy: RouteStrategy): void {
this.strategy = strategy;
}
public executeRouting(A: string, B: string): void {
// The navigator delegates the complex math to the strategy
this.strategy.buildRoute(A, B);
}
}
// Client Code
const navigator = new Navigator(new RoadStrategy());
navigator.executeRouting("Home", "Airport");
// Output: Calculating ROAD route...
// User clicked the "Walking" icon! Swap the strategy at runtime.
navigator.setStrategy(new WalkingStrategy());
navigator.executeRouting("Home", "Airport");
// Output: Calculating WALKING route...
Class Diagram
Why is it Important?
- Open/Closed Principle: You can add new algorithms without modifying the Context.
- Composition over Inheritance: Replaces inheritance (creating
CarNavigator,WalkingNavigator) with composition, making the system much more flexible.