Strategy Pattern
How it works
- Input arrives
- Core component processes
- Result produced
- Observe and iterate
Overview
Define a family of algorithms, encapsulate each one, and make them interchangeable.Learning Objectives
🎯 Algorithm Encapsulation
Master the technique of encapsulating algorithms and making them interchangeable at runtime
🔧 Behavioral Flexibility
Learn to eliminate conditional logic by using polymorphism and strategy selection
⚡ Open/Closed Principle
Understand how strategies enable adding new algorithms without modifying existing code
Real-World Examples
💰 Pricing Strategies
Amazon's pricing engine uses different strategies (regular, prime, bulk discount, seasonal) to calculate product prices dynamically
🚚 Route Optimization
Uber's routing system switches between strategies (fastest, shortest, eco-friendly) based on user preferences and traffic conditions
🔍 Search Algorithms
Google Search employs different ranking strategies (relevance, recency, authority) that can be combined and weighted dynamically
🎮 Game AI Behavior
Video game NPCs use strategy patterns for different behavior modes (aggressive, defensive, stealth) that change based on game state
Code example
Switch sorting strategies at runtime without changing the client code:interface SortStrategy<T> { sort(arr: T[]): T[] }
class QuickSortStrategy<T> implements SortStrategy<T> {
sort(arr: T[]): T[] { return arr.slice().sort() }
}
class ReverseSortStrategy<T> implements SortStrategy<T> {
sort(arr: T[]): T[] { return arr.slice().sort().reverse() }
}
class Sorter<T> {
constructor(private strategy: SortStrategy<T>) {}
setStrategy(s: SortStrategy<T>) { this.strategy = s }
run(arr: T[]): T[] { return this.strategy.sort(arr) }
}
var sorter = new Sorter(new QuickSortStrategy());
var a = sorter.run([3, 1, 2]); // [1,2,3]
sorter.setStrategy(new ReverseSortStrategy());
var b = sorter.run([3, 1, 2]); // [3,2,1]
Implementation Notes
- Define a common interface that all strategy implementations must follow
- Use dependency injection or factory patterns to provide strategy instances
- Consider using enum or configuration to map strategy names to implementations
- Implement null object pattern for default strategy behavior
- Use generic types when strategies work with different data types
Best Practices
- Keep strategies stateless and focused on single algorithmic concerns
- Use descriptive names that clearly indicate the strategy's behavior
- Provide clear documentation about when to use each strategy
- Consider strategy composition for complex scenarios requiring multiple algorithms
- Implement proper error handling and validation within each strategy
Common Pitfalls
- Creating too many strategies for minor algorithmic variations instead of parameterization
- Coupling strategies to specific data structures instead of using abstractions
- Forgetting to handle edge cases consistently across all strategy implementations
- Making strategies stateful which can lead to threading and reusability issues
- Not providing clear guidance on strategy selection criteria for users