Strategy Pattern
Strategy Pattern: Swapping Algorithms at Runtime
Section titled “Strategy Pattern: Swapping Algorithms at Runtime”Now let’s dive into the Strategy Pattern - one of the most practical behavioral design patterns that enables you to define a family of algorithms, encapsulate each one, and make them interchangeable at runtime.
Why Strategy Pattern?
Section titled “Why Strategy Pattern?”Imagine you’re using a GPS navigation app. You can choose different routes - fastest, shortest, avoid tolls, scenic route. Each routing algorithm is different, but they all solve the same problem: getting you from A to B. The Strategy Pattern works the same way!
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it, enabling you to swap behaviors at runtime without changing the client code.
What’s the Use of Strategy Pattern?
Section titled “What’s the Use of Strategy Pattern?”The Strategy Pattern is useful when:
- You have multiple algorithms for a specific task and want to switch between them
- You want to avoid conditionals - No more if/else or switch statements for algorithm selection
- You need runtime flexibility - Change algorithm without modifying client code
- You want to isolate algorithm code - Each strategy is in its own class
- You need to test algorithms independently - Easy to unit test each strategy
What Happens If We Don’t Use Strategy Pattern?
Section titled “What Happens If We Don’t Use Strategy Pattern?”Without the Strategy Pattern, you might:
- Use massive if/else chains - Hard to maintain and extend
- Violate Open/Closed Principle - Need to modify code to add new algorithms
- Duplicate code - Similar algorithms with slight variations
- Tight coupling - Client knows about all algorithm implementations
- Hard to test - Algorithms mixed with business logic
Simple Example: The Sorting Application
Section titled “Simple Example: The Sorting Application”Let’s start with a super simple example that anyone can understand!
Visual Representation
Section titled “Visual Representation”Interaction Flow
Section titled “Interaction Flow”Here’s how the Strategy Pattern works in practice - showing how strategies are swapped at runtime:
sequenceDiagram
participant Client
participant Context as SortingApplication
participant Strategy1 as QuickSort
participant Strategy2 as MergeSort
Client->>Context: set_strategy(QuickSort)
activate Context
Context->>Context: Store strategy reference
Context-->>Client: Strategy set
deactivate Context
Client->>Context: sort([3, 1, 4, 1, 5])
activate Context
Context->>Strategy1: sort([3, 1, 4, 1, 5])
activate Strategy1
Strategy1->>Strategy1: Execute QuickSort algorithm
Strategy1-->>Context: [1, 1, 3, 4, 5]
deactivate Strategy1
Context-->>Client: [1, 1, 3, 4, 5]
deactivate Context
Note over Client,Strategy2: Client can swap strategy at runtime!
Client->>Context: set_strategy(MergeSort)
Context-->>Client: Strategy changed
Client->>Context: sort([9, 7, 5, 3, 1])
activate Context
Context->>Strategy2: sort([9, 7, 5, 3, 1])
activate Strategy2
Strategy2->>Strategy2: Execute MergeSort algorithm
Strategy2-->>Context: [1, 3, 5, 7, 9]
deactivate Strategy2
Context-->>Client: [1, 3, 5, 7, 9]
deactivate Context
The Problem
Section titled “The Problem”You’re building a sorting utility that needs to support multiple sorting algorithms. Without Strategy Pattern:
Problems:
- Massive
if/elsechain - Hard to read and maintain - Violates Open/Closed Principle - Need to modify class to add algorithms
- Hard to test - All algorithms in one class
- Tight coupling - Client knows about algorithm details
The Solution: Strategy Pattern
Section titled “The Solution: Strategy Pattern”Class Structure
Section titled “Class Structure”classDiagram
class SortStrategy {
<<interface>>
+sort(data) List
}
class BubbleSortStrategy {
+sort(data) List
}
class QuickSortStrategy {
+sort(data) List
}
class MergeSortStrategy {
+sort(data) List
}
class SortingApplication {
-strategy: SortStrategy
+set_strategy(strategy) void
+sort(data) List
}
SortStrategy <|.. BubbleSortStrategy : implements
SortStrategy <|.. QuickSortStrategy : implements
SortStrategy <|.. MergeSortStrategy : implements
SortingApplication --> SortStrategy : uses
note for SortStrategy "All strategies implement\nthe same interface"
note for SortingApplication "Context delegates to\ncurrent strategy"
Real-World Software Example: Payment Processing System
Section titled “Real-World Software Example: Payment Processing System”Now let’s see a realistic software example - a payment processing system that supports multiple payment methods.
The Problem
Section titled “The Problem”You’re building an e-commerce checkout system that needs to support multiple payment methods - Credit Card, PayPal, and Cryptocurrency. Without Strategy Pattern:
Problems:
- Adding new payment methods requires modifying the processor class
- Different validation logic mixed in one class
- Hard to test individual payment methods
- Violates Single Responsibility and Open/Closed principles
The Solution: Strategy Pattern
Section titled “The Solution: Strategy Pattern”Class Structure
Section titled “Class Structure”classDiagram
class PaymentStrategy {
<<interface>>
+validate(details) bool
+process(amount, details) PaymentResult
}
class CreditCardStrategy {
+validate(details) bool
+process(amount, details) PaymentResult
}
class PayPalStrategy {
+validate(details) bool
+process(amount, details) PaymentResult
}
class CryptoStrategy {
+validate(details) bool
+process(amount, details) PaymentResult
}
class PaymentProcessor {
-strategy: PaymentStrategy
+set_strategy(strategy) void
+process_payment(amount, details) PaymentResult
}
PaymentStrategy <|.. CreditCardStrategy : implements
PaymentStrategy <|.. PayPalStrategy : implements
PaymentStrategy <|.. CryptoStrategy : implements
PaymentProcessor --> PaymentStrategy : uses
note for PaymentStrategy "Each payment method\nis a separate strategy"
note for PaymentProcessor "Delegates to current\npayment strategy"
Strategy Pattern Variants
Section titled “Strategy Pattern Variants”There are different ways to implement the Strategy Pattern:
1. Classic Strategy (Class-Based)
Section titled “1. Classic Strategy (Class-Based)”Using abstract classes/interfaces:
Pros: Type-safe, clear contracts, easy to extend
Cons: More classes to manage
2. Functional Strategy (Lambda-Based)
Section titled “2. Functional Strategy (Lambda-Based)”Using functions/lambdas:
Pros: Less boilerplate, more flexible
Cons: Less type-safe, harder to document
3. Strategy with Configuration
Section titled “3. Strategy with Configuration”Strategies that can be configured:
When to Use Strategy Pattern?
Section titled “When to Use Strategy Pattern?”Use Strategy Pattern when:
✅ You have multiple algorithms - Different ways to do the same thing
✅ You need runtime switching - Change algorithm based on user input or conditions
✅ You want to eliminate conditionals - Replace if/else or switch statements
✅ Algorithms should be interchangeable - Same interface, different implementations
✅ You need to isolate algorithm code - Each algorithm in its own class
When NOT to Use Strategy Pattern?
Section titled “When NOT to Use Strategy Pattern?”Don’t use Strategy Pattern when:
❌ Only one algorithm exists - No need to abstract
❌ Algorithm never changes - Static behavior is simpler
❌ Simple conditionals - 2-3 branches might be clearer without pattern
❌ Performance is critical - Indirection has small overhead
❌ Over-engineering - Don’t add complexity for hypothetical future needs
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”Mistake 1: Strategies That Share State
Section titled “Mistake 1: Strategies That Share State”Mistake 2: Context Exposing Strategy Details
Section titled “Mistake 2: Context Exposing Strategy Details”Mistake 3: Strategy Knowing About Context
Section titled “Mistake 3: Strategy Knowing About Context”Benefits of Strategy Pattern
Section titled “Benefits of Strategy Pattern”- Open/Closed Principle - Add new algorithms without modifying existing code
- Single Responsibility - Each algorithm in its own class
- Runtime Flexibility - Change algorithms dynamically
- Eliminates Conditionals - No more if/else chains
- Easy Testing - Test each strategy independently
- Code Reuse - Strategies can be reused across contexts
Revision: Quick Catch-Up
Section titled “Revision: Quick Catch-Up”What is Strategy Pattern?
Section titled “What is Strategy Pattern?”Strategy Pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it.
Why Use It?
Section titled “Why Use It?”- ✅ Multiple algorithms - Different ways to accomplish same task
- ✅ Runtime switching - Change algorithm based on conditions
- ✅ Eliminate conditionals - No if/else chains
- ✅ Easy testing - Test each strategy independently
- ✅ Follow Open/Closed Principle
How It Works?
Section titled “How It Works?”- Define Strategy interface - Common interface for all algorithms
- Create Concrete Strategies - Each algorithm in its own class
- Create Context - Uses a strategy through the interface
- Set Strategy - Context can change strategy at runtime
- Execute - Context delegates to current strategy
Key Components
Section titled “Key Components”Context → Strategy Interface → Concrete Strategies- Strategy - Interface for all algorithms
- Concrete Strategy - Specific algorithm implementation
- Context - Uses a strategy, allows switching
- Client - Configures context with strategy
Simple Example
Section titled “Simple Example”class Strategy(ABC): @abstractmethod def execute(self, data): pass
class ConcreteStrategy(Strategy): def execute(self, data): return process(data)
class Context: def __init__(self, strategy: Strategy): self._strategy = strategy
def set_strategy(self, strategy: Strategy): self._strategy = strategy
def do_work(self, data): return self._strategy.execute(data)When to Use?
Section titled “When to Use?”✅ Multiple algorithms for same task
✅ Need runtime algorithm switching
✅ Growing if/else chain for algorithm selection
✅ Want to test algorithms independently
✅ Algorithms should be interchangeable
When NOT to Use?
Section titled “When NOT to Use?”❌ Only one algorithm
❌ Algorithm never changes
❌ Simple 2-3 branch conditionals
❌ Over-engineering simple problems
Key Takeaways
Section titled “Key Takeaways”- Strategy Pattern = Interchangeable algorithms
- Strategy = Algorithm interface
- Context = Uses strategy, allows switching
- Benefit = Flexibility, testability, no conditionals
- Principle = Open for extension, closed for modification
Common Pattern Structure
Section titled “Common Pattern Structure”# 1. Strategy Interfaceclass Strategy(ABC): @abstractmethod def execute(self, data): pass
# 2. Concrete Strategiesclass StrategyA(Strategy): def execute(self, data): return process_a(data)
class StrategyB(Strategy): def execute(self, data): return process_b(data)
# 3. Contextclass Context: def __init__(self, strategy: Strategy): self._strategy = strategy
def set_strategy(self, strategy: Strategy): self._strategy = strategy
def execute(self, data): return self._strategy.execute(data)
# 4. Usagecontext = Context(StrategyA())context.execute(data)context.set_strategy(StrategyB())context.execute(data)Remember
Section titled “Remember”- Strategy Pattern encapsulates algorithms into separate classes
- It enables runtime switching of algorithms
- It follows Open/Closed Principle - easy to add new strategies
- Use it when you need multiple interchangeable algorithms
- Don’t use it for simple cases where if/else is clearer!
Interview Focus: Strategy Pattern
Section titled “Interview Focus: Strategy Pattern”Key Points to Remember
Section titled “Key Points to Remember”1. Core Concept
Section titled “1. Core Concept”What to say:
“Strategy Pattern is a behavioral design pattern that defines a family of algorithms, encapsulates each one in a separate class, and makes them interchangeable. The pattern lets the algorithm vary independently from clients that use it, enabling runtime behavior changes without modifying the client code.”
Why it matters:
- Shows you understand the fundamental purpose
- Demonstrates knowledge of encapsulation
- Indicates you can explain concepts clearly
2. When to Use Strategy Pattern
Section titled “2. When to Use Strategy Pattern”Must mention:
- ✅ Multiple algorithms - Different ways to accomplish the same task
- ✅ Runtime switching - Need to change behavior dynamically
- ✅ Eliminate conditionals - Replace if/else chains
- ✅ Testing isolation - Test each algorithm independently
- ✅ Open/Closed Principle - Add algorithms without modifying existing code
Example scenario to give:
“I’d use Strategy Pattern when building a payment processing system. Each payment method - Credit Card, PayPal, Crypto - is a different strategy. The checkout process doesn’t care which payment method is used; it just calls the pay() method. Users can switch payment methods at runtime, and adding new payment methods is just creating a new strategy class.”
3. Strategy vs State Pattern
Section titled “3. Strategy vs State Pattern”Must discuss:
- Strategy - Client chooses the strategy explicitly, algorithms are interchangeable
- State - Object changes behavior based on internal state automatically
- Key difference - Who controls the switch (client vs object) and why
Example to give:
“Strategy Pattern is like choosing a shipping method at checkout - YOU choose between standard, express, or overnight. State Pattern is like a vending machine - it changes behavior automatically based on whether it has items, received payment, etc. With Strategy, the client decides. With State, the object decides based on its state.”
4. SOLID Principles Connection
Section titled “4. SOLID Principles Connection”Must discuss:
- Open/Closed Principle - Add new strategies without modifying context
- Single Responsibility - Each strategy handles one algorithm
- Dependency Inversion - Context depends on Strategy abstraction
- Interface Segregation - Strategy interface is focused and small
Example to give:
“Strategy Pattern strongly supports the Open/Closed Principle - you can add new sorting algorithms without modifying the SortingApplication class. It also supports Single Responsibility because each strategy class has one job - implementing one specific algorithm. The context depends on the Strategy interface, not concrete implementations, supporting Dependency Inversion.”
5. Benefits and Trade-offs
Section titled “5. Benefits and Trade-offs”Benefits to mention:
- Runtime flexibility - Change algorithms dynamically
- No conditionals - Eliminate if/else chains
- Easy testing - Test each strategy independently
- Code organization - Each algorithm in its own class
- Reusability - Strategies can be reused across contexts
Trade-offs to acknowledge:
- More classes - Each algorithm is a separate class
- Client awareness - Client must know about different strategies
- Complexity for simple cases - Overkill for 2-3 simple algorithms
- Configuration overhead - Need to configure and inject strategies
6. Common Interview Questions
Section titled “6. Common Interview Questions”Q: “How does Strategy Pattern eliminate conditionals?”
A:
“Instead of having a switch statement or if/else chain that checks which algorithm to use, we delegate to a strategy object. The context just calls strategy.execute() - it doesn’t know or care which concrete strategy is being used. Adding a new algorithm doesn’t require modifying any existing code, just creating a new strategy class.”
Q: “When would you NOT use Strategy Pattern?”
A:
“I wouldn’t use Strategy Pattern when there’s only one algorithm, when the algorithm never changes, or for simple 2-3 branch conditionals where the overhead isn’t justified. The pattern adds complexity with multiple classes, so for simple cases, a direct if/else might be clearer and more maintainable.”
Q: “How does Strategy Pattern relate to Dependency Injection?”
A:
“Strategy Pattern and Dependency Injection work together beautifully. The strategy is injected into the context, allowing the algorithm to be configured externally. This makes the context more flexible and testable - you can inject mock strategies in tests. In frameworks like Spring, strategies are often injected through constructor injection.”
Interview Checklist
Section titled “Interview Checklist”Before your interview, make sure you can:
- Define Strategy Pattern clearly in one sentence
- Explain when to use it (with examples)
- Describe the structure: Strategy, Concrete Strategy, Context
- Implement Strategy Pattern from scratch
- Compare with State Pattern
- List benefits and trade-offs
- Connect to SOLID principles
- Identify when NOT to use it
- Give 2-3 real-world examples (payment, sorting, compression)
- Discuss functional vs class-based strategies
Remember: Strategy Pattern is about interchangeable algorithms - define a family of algorithms, encapsulate each one, and swap them at runtime! 🔄