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:
1# ❌ Without Strategy Pattern - Massive if/else chain!2
3from typing import List4
5class SortingApplication:6 def __init__(self):7 self.algorithm = "quicksort" # Default algorithm8
9 def set_algorithm(self, algorithm: str):10 self.algorithm = algorithm11
12 def sort(self, data: List[int]) -> List[int]:13 # Problem: Massive if/else chain!14 if self.algorithm == "bubble":15 # Bubble sort implementation16 result = data.copy()17 n = len(result)18 for i in range(n):19 for j in range(0, n - i - 1):20 if result[j] > result[j + 1]:21 result[j], result[j + 1] = result[j + 1], result[j]22 return result23
24 elif self.algorithm == "quick":25 # Quick sort implementation26 def quicksort(arr):27 if len(arr) <= 1:28 return arr29 pivot = arr[len(arr) // 2]30 left = [x for x in arr if x < pivot]31 middle = [x for x in arr if x == pivot]32 right = [x for x in arr if x > pivot]33 return quicksort(left) + middle + quicksort(right)34 return quicksort(data)35
36 elif self.algorithm == "merge":37 # Merge sort implementation38 def mergesort(arr):39 if len(arr) <= 1:40 return arr41 mid = len(arr) // 242 left = mergesort(arr[:mid])43 right = mergesort(arr[mid:])44 return merge(left, right)45
46 def merge(left, right):47 result = []48 i = j = 049 while i < len(left) and j < len(right):50 if left[i] <= right[j]:51 result.append(left[i])52 i += 153 else:54 result.append(right[j])55 j += 156 result.extend(left[i:])57 result.extend(right[j:])58 return result59
60 return mergesort(data)61
62 else:63 raise ValueError(f"Unknown algorithm: {self.algorithm}")64
65 # Problems:66 # - Adding new algorithm requires modifying this class67 # - Violates Open/Closed Principle68 # - Hard to test individual algorithms69 # - Code is hard to read and maintain70
71# Usage72app = SortingApplication()73app.set_algorithm("bubble")74print(app.sort([3, 1, 4, 1, 5]))1// ❌ Without Strategy Pattern - Massive if/else chain!2
3import java.util.*;4
5public class SortingApplication {6 private String algorithm = "quicksort"; // Default algorithm7
8 public void setAlgorithm(String algorithm) {9 this.algorithm = algorithm;10 }11
12 public int[] sort(int[] data) {13 // Problem: Massive if/else chain!14 if (algorithm.equals("bubble")) {15 // Bubble sort implementation16 int[] result = data.clone();17 int n = result.length;18 for (int i = 0; i < n - 1; i++) {19 for (int j = 0; j < n - i - 1; j++) {20 if (result[j] > result[j + 1]) {21 int temp = result[j];22 result[j] = result[j + 1];23 result[j + 1] = temp;24 }25 }26 }27 return result;28 } else if (algorithm.equals("quick")) {29 // Quick sort implementation30 int[] result = data.clone();31 quickSort(result, 0, result.length - 1);32 return result;33 } else if (algorithm.equals("merge")) {34 // Merge sort implementation35 int[] result = data.clone();36 mergeSort(result, 0, result.length - 1);37 return result;38 } else {39 throw new IllegalArgumentException("Unknown algorithm: " + algorithm);40 }41
42 // Problems:43 // - Adding new algorithm requires modifying this class44 // - Violates Open/Closed Principle45 // - Hard to test individual algorithms46 // - Code is hard to read and maintain47 }48
49 private void quickSort(int[] arr, int low, int high) {50 // QuickSort implementation51 if (low < high) {52 int pi = partition(arr, low, high);53 quickSort(arr, low, pi - 1);54 quickSort(arr, pi + 1, high);55 }56 }57
58 private int partition(int[] arr, int low, int high) {59 int pivot = arr[high];60 int i = low - 1;61 for (int j = low; j < high; j++) {62 if (arr[j] < pivot) {63 i++;64 int temp = arr[i];65 arr[i] = arr[j];66 arr[j] = temp;67 }68 }69 int temp = arr[i + 1];70 arr[i + 1] = arr[high];71 arr[high] = temp;72 return i + 1;73 }74
75 private void mergeSort(int[] arr, int l, int r) {76 // MergeSort implementation77 if (l < r) {78 int m = l + (r - l) / 2;79 mergeSort(arr, l, m);80 mergeSort(arr, m + 1, r);81 merge(arr, l, m, r);82 }83 }84
85 private void merge(int[] arr, int l, int m, int r) {86 // Merge two subarrays87 int n1 = m - l + 1;88 int n2 = r - m;89 int[] L = new int[n1];90 int[] R = new int[n2];91
92 System.arraycopy(arr, l, L, 0, n1);93 System.arraycopy(arr, m + 1, R, 0, n2);94
95 int i = 0, j = 0, k = l;96 while (i < n1 && j < n2) {97 if (L[i] <= R[j]) {98 arr[k++] = L[i++];99 } else {100 arr[k++] = R[j++];101 }102 }103 while (i < n1) arr[k++] = L[i++];104 while (j < n2) arr[k++] = R[j++];105 }106}107
108// Usage109public class Main {110 public static void main(String[] args) {111 SortingApplication app = new SortingApplication();112 app.setAlgorithm("bubble");113 System.out.println(Arrays.toString(app.sort(new int[]{3, 1, 4, 1, 5})));114 }115}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"
1from abc import ABC, abstractmethod2from typing import List3
4# Step 1: Define the Strategy interface5class SortStrategy(ABC):6 """Strategy interface for sorting algorithms"""7
8 @abstractmethod9 def sort(self, data: List[int]) -> List[int]:10 """Sort the data and return sorted list"""11 pass12
13# Step 2: Implement Concrete Strategies14class BubbleSortStrategy(SortStrategy):15 """Bubble sort strategy - O(n²) but simple"""16
17 def sort(self, data: List[int]) -> List[int]:18 result = data.copy()19 n = len(result)20 for i in range(n):21 for j in range(0, n - i - 1):22 if result[j] > result[j + 1]:23 result[j], result[j + 1] = result[j + 1], result[j]24 print("📊 Using Bubble Sort (O(n²) - good for small datasets)")25 return result26
27class QuickSortStrategy(SortStrategy):28 """Quick sort strategy - O(n log n) average"""29
30 def sort(self, data: List[int]) -> List[int]:31 def quicksort(arr: List[int]) -> List[int]:32 if len(arr) <= 1:33 return arr34 pivot = arr[len(arr) // 2]35 left = [x for x in arr if x < pivot]36 middle = [x for x in arr if x == pivot]37 right = [x for x in arr if x > pivot]38 return quicksort(left) + middle + quicksort(right)39
40 print("⚡ Using Quick Sort (O(n log n) - fast for most cases)")41 return quicksort(data)42
43class MergeSortStrategy(SortStrategy):44 """Merge sort strategy - O(n log n) guaranteed"""45
46 def sort(self, data: List[int]) -> List[int]:47 def mergesort(arr: List[int]) -> List[int]:48 if len(arr) <= 1:49 return arr50 mid = len(arr) // 251 left = mergesort(arr[:mid])52 right = mergesort(arr[mid:])53 return self._merge(left, right)54
55 print("🔀 Using Merge Sort (O(n log n) - stable, predictable)")56 return mergesort(data)57
58 def _merge(self, left: List[int], right: List[int]) -> List[int]:59 result = []60 i = j = 061 while i < len(left) and j < len(right):62 if left[i] <= right[j]:63 result.append(left[i])64 i += 165 else:66 result.append(right[j])67 j += 168 result.extend(left[i:])69 result.extend(right[j:])70 return result71
72# Step 3: Create the Context class73class SortingApplication:74 """Context class that uses sorting strategies"""75
76 def __init__(self, strategy: SortStrategy = None):77 self._strategy = strategy or QuickSortStrategy() # Default strategy78
79 def set_strategy(self, strategy: SortStrategy) -> None:80 """Change the sorting strategy at runtime"""81 self._strategy = strategy82 print(f"✅ Strategy changed to: {strategy.__class__.__name__}")83
84 def sort(self, data: List[int]) -> List[int]:85 """Sort data using the current strategy"""86 print(f"\n🔄 Sorting {data}")87 result = self._strategy.sort(data)88 print(f"✨ Result: {result}")89 return result90
91# Step 4: Use the pattern92def main():93 # Create context with default strategy94 app = SortingApplication()95
96 data = [64, 34, 25, 12, 22, 11, 90]97
98 # Sort with default (QuickSort)99 app.sort(data)100
101 # Switch to BubbleSort102 app.set_strategy(BubbleSortStrategy())103 app.sort(data)104
105 # Switch to MergeSort106 app.set_strategy(MergeSortStrategy())107 app.sort(data)108
109 print("\n✅ Strategy Pattern: Algorithms swapped at runtime!")110
111if __name__ == "__main__":112 main()1import java.util.*;2
3// Step 1: Define the Strategy interface4interface SortStrategy {5 /**6 * Strategy interface for sorting algorithms7 */8 int[] sort(int[] data);9}10
11// Step 2: Implement Concrete Strategies12class BubbleSortStrategy implements SortStrategy {13 /**14 * Bubble sort strategy - O(n²) but simple15 */16 @Override17 public int[] sort(int[] data) {18 int[] result = data.clone();19 int n = result.length;20 for (int i = 0; i < n - 1; i++) {21 for (int j = 0; j < n - i - 1; j++) {22 if (result[j] > result[j + 1]) {23 int temp = result[j];24 result[j] = result[j + 1];25 result[j + 1] = temp;26 }27 }28 }29 System.out.println("📊 Using Bubble Sort (O(n²) - good for small datasets)");30 return result;31 }32}33
34class QuickSortStrategy implements SortStrategy {35 /**36 * Quick sort strategy - O(n log n) average37 */38 @Override39 public int[] sort(int[] data) {40 int[] result = data.clone();41 quickSort(result, 0, result.length - 1);42 System.out.println("⚡ Using Quick Sort (O(n log n) - fast for most cases)");43 return result;44 }45
46 private void quickSort(int[] arr, int low, int high) {47 if (low < high) {48 int pi = partition(arr, low, high);49 quickSort(arr, low, pi - 1);50 quickSort(arr, pi + 1, high);51 }52 }53
54 private int partition(int[] arr, int low, int high) {55 int pivot = arr[high];56 int i = low - 1;57 for (int j = low; j < high; j++) {58 if (arr[j] < pivot) {59 i++;60 int temp = arr[i];61 arr[i] = arr[j];62 arr[j] = temp;63 }64 }65 int temp = arr[i + 1];66 arr[i + 1] = arr[high];67 arr[high] = temp;68 return i + 1;69 }70}71
72class MergeSortStrategy implements SortStrategy {73 /**74 * Merge sort strategy - O(n log n) guaranteed75 */76 @Override77 public int[] sort(int[] data) {78 int[] result = data.clone();79 mergeSort(result, 0, result.length - 1);80 System.out.println("🔀 Using Merge Sort (O(n log n) - stable, predictable)");81 return result;82 }83
84 private void mergeSort(int[] arr, int l, int r) {85 if (l < r) {86 int m = l + (r - l) / 2;87 mergeSort(arr, l, m);88 mergeSort(arr, m + 1, r);89 merge(arr, l, m, r);90 }91 }92
93 private void merge(int[] arr, int l, int m, int r) {94 int n1 = m - l + 1;95 int n2 = r - m;96 int[] L = new int[n1];97 int[] R = new int[n2];98
99 System.arraycopy(arr, l, L, 0, n1);100 System.arraycopy(arr, m + 1, R, 0, n2);101
102 int i = 0, j = 0, k = l;103 while (i < n1 && j < n2) {104 if (L[i] <= R[j]) {105 arr[k++] = L[i++];106 } else {107 arr[k++] = R[j++];108 }109 }110 while (i < n1) arr[k++] = L[i++];111 while (j < n2) arr[k++] = R[j++];112 }113}114
115// Step 3: Create the Context class116class SortingApplication {117 /**118 * Context class that uses sorting strategies119 */120 private SortStrategy strategy;121
122 public SortingApplication() {123 this.strategy = new QuickSortStrategy(); // Default strategy124 }125
126 public SortingApplication(SortStrategy strategy) {127 this.strategy = strategy;128 }129
130 public void setStrategy(SortStrategy strategy) {131 // Change the sorting strategy at runtime132 this.strategy = strategy;133 System.out.println("✅ Strategy changed to: " + strategy.getClass().getSimpleName());134 }135
136 public int[] sort(int[] data) {137 // Sort data using the current strategy138 System.out.println("\n🔄 Sorting " + Arrays.toString(data));139 int[] result = strategy.sort(data);140 System.out.println("✨ Result: " + Arrays.toString(result));141 return result;142 }143}144
145// Step 4: Use the pattern146public class Main {147 public static void main(String[] args) {148 // Create context with default strategy149 SortingApplication app = new SortingApplication();150
151 int[] data = {64, 34, 25, 12, 22, 11, 90};152
153 // Sort with default (QuickSort)154 app.sort(data);155
156 // Switch to BubbleSort157 app.setStrategy(new BubbleSortStrategy());158 app.sort(data);159
160 // Switch to MergeSort161 app.setStrategy(new MergeSortStrategy());162 app.sort(data);163
164 System.out.println("\n✅ Strategy Pattern: Algorithms swapped at runtime!");165 }166}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:
1# ❌ Without Strategy Pattern - if/else nightmare!2
3class PaymentProcessor:4 def __init__(self):5 self.payment_method = "credit_card"6
7 def set_payment_method(self, method: str):8 self.payment_method = method9
10 def process_payment(self, amount: float, details: dict) -> bool:11 # Problem: Massive if/else chain!12 if self.payment_method == "credit_card":13 card_number = details.get("card_number")14 cvv = details.get("cvv")15 expiry = details.get("expiry")16
17 # Validate card18 if not card_number or len(card_number) != 16:19 raise ValueError("Invalid card number")20 if not cvv or len(cvv) != 3:21 raise ValueError("Invalid CVV")22
23 # Process credit card payment24 print(f"💳 Processing credit card payment of ${amount}")25 print(f" Card: **** **** **** {card_number[-4:]}")26 # Connect to payment gateway...27 return True28
29 elif self.payment_method == "paypal":30 email = details.get("email")31
32 # Validate PayPal33 if not email or "@" not in email:34 raise ValueError("Invalid PayPal email")35
36 # Process PayPal payment37 print(f"🅿️ Processing PayPal payment of ${amount}")38 print(f" Email: {email}")39 # Redirect to PayPal...40 return True41
42 elif self.payment_method == "crypto":43 wallet_address = details.get("wallet_address")44 currency = details.get("currency", "BTC")45
46 # Validate crypto47 if not wallet_address or len(wallet_address) < 26:48 raise ValueError("Invalid wallet address")49
50 # Process crypto payment51 print(f"₿ Processing {currency} payment of ${amount}")52 print(f" Wallet: {wallet_address[:10]}...")53 # Generate crypto invoice...54 return True55
56 else:57 raise ValueError(f"Unknown payment method: {self.payment_method}")58
59 # Problems:60 # - Adding new payment method requires modifying this class61 # - Each payment method has different validation logic62 # - Hard to test individual payment methods63 # - Violates Single Responsibility Principle64
65# Usage66processor = PaymentProcessor()67processor.set_payment_method("credit_card")68processor.process_payment(99.99, {"card_number": "1234567890123456", "cvv": "123", "expiry": "12/25"})1// ❌ Without Strategy Pattern - if/else nightmare!2
3import java.util.Map;4
5public class PaymentProcessor {6 private String paymentMethod = "credit_card";7
8 public void setPaymentMethod(String method) {9 this.paymentMethod = method;10 }11
12 public boolean processPayment(double amount, Map<String, String> details) {13 // Problem: Massive if/else chain!14 if (paymentMethod.equals("credit_card")) {15 String cardNumber = details.get("card_number");16 String cvv = details.get("cvv");17 String expiry = details.get("expiry");18
19 // Validate card20 if (cardNumber == null || cardNumber.length() != 16) {21 throw new IllegalArgumentException("Invalid card number");22 }23 if (cvv == null || cvv.length() != 3) {24 throw new IllegalArgumentException("Invalid CVV");25 }26
27 // Process credit card payment28 System.out.println("💳 Processing credit card payment of $" + amount);29 System.out.println(" Card: **** **** **** " + cardNumber.substring(12));30 // Connect to payment gateway...31 return true;32
33 } else if (paymentMethod.equals("paypal")) {34 String email = details.get("email");35
36 // Validate PayPal37 if (email == null || !email.contains("@")) {38 throw new IllegalArgumentException("Invalid PayPal email");39 }40
41 // Process PayPal payment42 System.out.println("🅿️ Processing PayPal payment of $" + amount);43 System.out.println(" Email: " + email);44 // Redirect to PayPal...45 return true;46
47 } else if (paymentMethod.equals("crypto")) {48 String walletAddress = details.get("wallet_address");49 String currency = details.getOrDefault("currency", "BTC");50
51 // Validate crypto52 if (walletAddress == null || walletAddress.length() < 26) {53 throw new IllegalArgumentException("Invalid wallet address");54 }55
56 // Process crypto payment57 System.out.println("₿ Processing " + currency + " payment of $" + amount);58 System.out.println(" Wallet: " + walletAddress.substring(0, 10) + "...");59 // Generate crypto invoice...60 return true;61
62 } else {63 throw new IllegalArgumentException("Unknown payment method: " + paymentMethod);64 }65
66 // Problems:67 // - Adding new payment method requires modifying this class68 // - Each payment method has different validation logic69 // - Hard to test individual payment methods70 // - Violates Single Responsibility Principle71 }72}73
74// Usage75public class Main {76 public static void main(String[] args) {77 PaymentProcessor processor = new PaymentProcessor();78 processor.setPaymentMethod("credit_card");79 processor.processPayment(99.99, Map.of(80 "card_number", "1234567890123456",81 "cvv", "123",82 "expiry", "12/25"83 ));84 }85}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"
1from abc import ABC, abstractmethod2from dataclasses import dataclass3from typing import Dict, Any4from enum import Enum5
6# Step 1: Define result classes7class PaymentStatus(Enum):8 SUCCESS = "success"9 FAILED = "failed"10 PENDING = "pending"11
12@dataclass13class PaymentResult:14 """Result of a payment operation"""15 status: PaymentStatus16 transaction_id: str17 message: str18
19# Step 2: Define the Strategy interface20class PaymentStrategy(ABC):21 """Strategy interface for payment methods"""22
23 @abstractmethod24 def validate(self, details: Dict[str, Any]) -> bool:25 """Validate payment details"""26 pass27
28 @abstractmethod29 def process(self, amount: float, details: Dict[str, Any]) -> PaymentResult:30 """Process the payment"""31 pass32
33# Step 3: Implement Concrete Strategies34class CreditCardStrategy(PaymentStrategy):35 """Credit card payment strategy"""36
37 def validate(self, details: Dict[str, Any]) -> bool:38 card_number = details.get("card_number", "")39 cvv = details.get("cvv", "")40 expiry = details.get("expiry", "")41
42 if len(card_number) != 16 or not card_number.isdigit():43 raise ValueError("Invalid card number - must be 16 digits")44 if len(cvv) != 3 or not cvv.isdigit():45 raise ValueError("Invalid CVV - must be 3 digits")46 if not expiry or "/" not in expiry:47 raise ValueError("Invalid expiry date - use MM/YY format")48
49 return True50
51 def process(self, amount: float, details: Dict[str, Any]) -> PaymentResult:52 self.validate(details)53
54 card_number = details["card_number"]55 print(f"💳 Processing credit card payment")56 print(f" Amount: ${amount:.2f}")57 print(f" Card: **** **** **** {card_number[-4:]}")58 print(f" Connecting to payment gateway...")59
60 # Simulate payment processing61 transaction_id = f"CC-{card_number[-4:]}-{int(amount * 100)}"62
63 return PaymentResult(64 status=PaymentStatus.SUCCESS,65 transaction_id=transaction_id,66 message="Credit card payment successful"67 )68
69class PayPalStrategy(PaymentStrategy):70 """PayPal payment strategy"""71
72 def validate(self, details: Dict[str, Any]) -> bool:73 email = details.get("email", "")74
75 if not email or "@" not in email:76 raise ValueError("Invalid PayPal email address")77
78 return True79
80 def process(self, amount: float, details: Dict[str, Any]) -> PaymentResult:81 self.validate(details)82
83 email = details["email"]84 print(f"🅿️ Processing PayPal payment")85 print(f" Amount: ${amount:.2f}")86 print(f" Email: {email}")87 print(f" Redirecting to PayPal...")88
89 # Simulate payment processing90 transaction_id = f"PP-{email.split('@')[0]}-{int(amount * 100)}"91
92 return PaymentResult(93 status=PaymentStatus.SUCCESS,94 transaction_id=transaction_id,95 message="PayPal payment successful"96 )97
98class CryptoStrategy(PaymentStrategy):99 """Cryptocurrency payment strategy"""100
101 def validate(self, details: Dict[str, Any]) -> bool:102 wallet_address = details.get("wallet_address", "")103
104 if len(wallet_address) < 26:105 raise ValueError("Invalid wallet address - too short")106
107 return True108
109 def process(self, amount: float, details: Dict[str, Any]) -> PaymentResult:110 self.validate(details)111
112 wallet_address = details["wallet_address"]113 currency = details.get("currency", "BTC")114 print(f"₿ Processing {currency} payment")115 print(f" Amount: ${amount:.2f}")116 print(f" Wallet: {wallet_address[:10]}...{wallet_address[-4:]}")117 print(f" Generating invoice...")118
119 # Simulate payment processing120 transaction_id = f"CRYPTO-{currency}-{int(amount * 100)}"121
122 return PaymentResult(123 status=PaymentStatus.PENDING,124 transaction_id=transaction_id,125 message=f"Awaiting {currency} confirmation"126 )127
128# Step 4: Create the Context class129class PaymentProcessor:130 """Context class that uses payment strategies"""131
132 def __init__(self, strategy: PaymentStrategy = None):133 self._strategy = strategy or CreditCardStrategy() # Default134
135 def set_strategy(self, strategy: PaymentStrategy) -> None:136 """Change payment strategy at runtime"""137 self._strategy = strategy138 print(f"\n✅ Payment method changed to: {strategy.__class__.__name__}")139
140 def process_payment(self, amount: float, details: Dict[str, Any]) -> PaymentResult:141 """Process payment using current strategy"""142 print(f"\n{'='*50}")143 print(f"Processing payment of ${amount:.2f}")144 print(f"{'='*50}")145
146 result = self._strategy.process(amount, details)147
148 print(f"\n📋 Result: {result.status.value}")149 print(f"📋 Transaction ID: {result.transaction_id}")150 print(f"📋 Message: {result.message}")151
152 return result153
154# Step 5: Use the pattern155def main():156 # Create payment processor157 processor = PaymentProcessor()158
159 # Process credit card payment160 processor.process_payment(99.99, {161 "card_number": "4532015112830366",162 "cvv": "123",163 "expiry": "12/25"164 })165
166 # Switch to PayPal167 processor.set_strategy(PayPalStrategy())168 processor.process_payment(49.99, {169 "email": "customer@example.com"170 })171
172 # Switch to Crypto173 processor.set_strategy(CryptoStrategy())174 processor.process_payment(199.99, {175 "wallet_address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",176 "currency": "BTC"177 })178
179 # Easy to add new payment method - just create new strategy!180 print("\n\n✅ Strategy Pattern: Payment methods swapped at runtime!")181 print("✅ Adding new payment method = Just create a new strategy class!")182
183if __name__ == "__main__":184 main()1import java.util.*;2
3// Step 1: Define result classes4enum PaymentStatus {5 SUCCESS, FAILED, PENDING6}7
8class PaymentResult {9 /**10 * Result of a payment operation11 */12 private PaymentStatus status;13 private String transactionId;14 private String message;15
16 public PaymentResult(PaymentStatus status, String transactionId, String message) {17 this.status = status;18 this.transactionId = transactionId;19 this.message = message;20 }21
22 public PaymentStatus getStatus() { return status; }23 public String getTransactionId() { return transactionId; }24 public String getMessage() { return message; }25}26
27// Step 2: Define the Strategy interface28interface PaymentStrategy {29 /**30 * Strategy interface for payment methods31 */32 boolean validate(Map<String, String> details);33 PaymentResult process(double amount, Map<String, String> details);34}35
36// Step 3: Implement Concrete Strategies37class CreditCardStrategy implements PaymentStrategy {38 /**39 * Credit card payment strategy40 */41 @Override42 public boolean validate(Map<String, String> details) {43 String cardNumber = details.getOrDefault("card_number", "");44 String cvv = details.getOrDefault("cvv", "");45 String expiry = details.getOrDefault("expiry", "");46
47 if (cardNumber.length() != 16 || !cardNumber.matches("\\d+")) {48 throw new IllegalArgumentException("Invalid card number - must be 16 digits");49 }50 if (cvv.length() != 3 || !cvv.matches("\\d+")) {51 throw new IllegalArgumentException("Invalid CVV - must be 3 digits");52 }53 if (expiry.isEmpty() || !expiry.contains("/")) {54 throw new IllegalArgumentException("Invalid expiry date - use MM/YY format");55 }56
57 return true;58 }59
60 @Override61 public PaymentResult process(double amount, Map<String, String> details) {62 validate(details);63
64 String cardNumber = details.get("card_number");65 System.out.println("💳 Processing credit card payment");66 System.out.printf(" Amount: $%.2f%n", amount);67 System.out.println(" Card: **** **** **** " + cardNumber.substring(12));68 System.out.println(" Connecting to payment gateway...");69
70 String transactionId = String.format("CC-%s-%d",71 cardNumber.substring(12), (int)(amount * 100));72
73 return new PaymentResult(74 PaymentStatus.SUCCESS,75 transactionId,76 "Credit card payment successful"77 );78 }79}80
81class PayPalStrategy implements PaymentStrategy {82 /**83 * PayPal payment strategy84 */85 @Override86 public boolean validate(Map<String, String> details) {87 String email = details.getOrDefault("email", "");88
89 if (email.isEmpty() || !email.contains("@")) {90 throw new IllegalArgumentException("Invalid PayPal email address");91 }92
93 return true;94 }95
96 @Override97 public PaymentResult process(double amount, Map<String, String> details) {98 validate(details);99
100 String email = details.get("email");101 System.out.println("🅿️ Processing PayPal payment");102 System.out.printf(" Amount: $%.2f%n", amount);103 System.out.println(" Email: " + email);104 System.out.println(" Redirecting to PayPal...");105
106 String transactionId = String.format("PP-%s-%d",107 email.split("@")[0], (int)(amount * 100));108
109 return new PaymentResult(110 PaymentStatus.SUCCESS,111 transactionId,112 "PayPal payment successful"113 );114 }115}116
117class CryptoStrategy implements PaymentStrategy {118 /**119 * Cryptocurrency payment strategy120 */121 @Override122 public boolean validate(Map<String, String> details) {123 String walletAddress = details.getOrDefault("wallet_address", "");124
125 if (walletAddress.length() < 26) {126 throw new IllegalArgumentException("Invalid wallet address - too short");127 }128
129 return true;130 }131
132 @Override133 public PaymentResult process(double amount, Map<String, String> details) {134 validate(details);135
136 String walletAddress = details.get("wallet_address");137 String currency = details.getOrDefault("currency", "BTC");138 System.out.println("₿ Processing " + currency + " payment");139 System.out.printf(" Amount: $%.2f%n", amount);140 System.out.println(" Wallet: " + walletAddress.substring(0, 10) + "..." +141 walletAddress.substring(walletAddress.length() - 4));142 System.out.println(" Generating invoice...");143
144 String transactionId = String.format("CRYPTO-%s-%d",145 currency, (int)(amount * 100));146
147 return new PaymentResult(148 PaymentStatus.PENDING,149 transactionId,150 "Awaiting " + currency + " confirmation"151 );152 }153}154
155// Step 4: Create the Context class156class PaymentProcessor {157 /**158 * Context class that uses payment strategies159 */160 private PaymentStrategy strategy;161
162 public PaymentProcessor() {163 this.strategy = new CreditCardStrategy(); // Default164 }165
166 public PaymentProcessor(PaymentStrategy strategy) {167 this.strategy = strategy;168 }169
170 public void setStrategy(PaymentStrategy strategy) {171 // Change payment strategy at runtime172 this.strategy = strategy;173 System.out.println("\n✅ Payment method changed to: " +174 strategy.getClass().getSimpleName());175 }176
177 public PaymentResult processPayment(double amount, Map<String, String> details) {178 // Process payment using current strategy179 System.out.println("\n" + "=".repeat(50));180 System.out.printf("Processing payment of $%.2f%n", amount);181 System.out.println("=".repeat(50));182
183 PaymentResult result = strategy.process(amount, details);184
185 System.out.println("\n📋 Result: " + result.getStatus());186 System.out.println("📋 Transaction ID: " + result.getTransactionId());187 System.out.println("📋 Message: " + result.getMessage());188
189 return result;190 }191}192
193// Step 5: Use the pattern194public class Main {195 public static void main(String[] args) {196 // Create payment processor197 PaymentProcessor processor = new PaymentProcessor();198
199 // Process credit card payment200 processor.processPayment(99.99, Map.of(201 "card_number", "4532015112830366",202 "cvv", "123",203 "expiry", "12/25"204 ));205
206 // Switch to PayPal207 processor.setStrategy(new PayPalStrategy());208 processor.processPayment(49.99, Map.of(209 "email", "customer@example.com"210 ));211
212 // Switch to Crypto213 processor.setStrategy(new CryptoStrategy());214 processor.processPayment(199.99, Map.of(215 "wallet_address", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",216 "currency", "BTC"217 ));218
219 // Easy to add new payment method - just create new strategy!220 System.out.println("\n\n✅ Strategy Pattern: Payment methods swapped at runtime!");221 System.out.println("✅ Adding new payment method = Just create a new strategy class!");222 }223}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:
1# Classic Strategy - class-based2from abc import ABC, abstractmethod3
4class Strategy(ABC):5 @abstractmethod6 def execute(self, data): pass7
8class ConcreteStrategyA(Strategy):9 def execute(self, data):10 return f"Strategy A: {data}"11
12class Context:13 def __init__(self, strategy: Strategy):14 self._strategy = strategy15
16 def do_something(self, data):17 return self._strategy.execute(data)1// Classic Strategy - class-based2interface Strategy {3 String execute(String data);4}5
6class ConcreteStrategyA implements Strategy {7 @Override8 public String execute(String data) {9 return "Strategy A: " + data;10 }11}12
13class Context {14 private Strategy strategy;15
16 public Context(Strategy strategy) {17 this.strategy = strategy;18 }19
20 public String doSomething(String data) {21 return strategy.execute(data);22 }23}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:
1# Functional Strategy - using functions2from typing import Callable3
4class Context:5 def __init__(self, strategy: Callable):6 self._strategy = strategy7
8 def do_something(self, data):9 return self._strategy(data)10
11# Use lambda or function as strategy12context = Context(lambda data: f"Strategy A: {data}")13print(context.do_something("Hello"))14
15# Change strategy16context._strategy = lambda data: f"Strategy B: {data.upper()}"17print(context.do_something("Hello"))1// Functional Strategy - using lambdas2import java.util.function.Function;3
4class Context {5 private Function<String, String> strategy;6
7 public Context(Function<String, String> strategy) {8 this.strategy = strategy;9 }10
11 public void setStrategy(Function<String, String> strategy) {12 this.strategy = strategy;13 }14
15 public String doSomething(String data) {16 return strategy.apply(data);17 }18}19
20// Use lambda as strategy21public class Main {22 public static void main(String[] args) {23 Context context = new Context(data -> "Strategy A: " + data);24 System.out.println(context.doSomething("Hello"));25
26 // Change strategy27 context.setStrategy(data -> "Strategy B: " + data.toUpperCase());28 System.out.println(context.doSomething("Hello"));29 }30}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:
1# Strategy with configuration2class CompressionStrategy(ABC):3 @abstractmethod4 def compress(self, data: bytes) -> bytes: pass5
6class GzipStrategy(CompressionStrategy):7 def __init__(self, level: int = 6):8 self.level = level # Compression level 1-99
10 def compress(self, data: bytes) -> bytes:11 import gzip12 return gzip.compress(data, compresslevel=self.level)13
14# Usage - strategy with different configurations15fast_compression = GzipStrategy(level=1)16max_compression = GzipStrategy(level=9)1// Strategy with configuration2interface CompressionStrategy {3 byte[] compress(byte[] data);4}5
6class GzipStrategy implements CompressionStrategy {7 private int level; // Compression level 1-98
9 public GzipStrategy(int level) {10 this.level = level;11 }12
13 @Override14 public byte[] compress(byte[] data) {15 // Use compression level...16 return data; // Simplified17 }18}19
20// Usage - strategy with different configurations21CompressionStrategy fastCompression = new GzipStrategy(1);22CompressionStrategy maxCompression = new GzipStrategy(9);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”1# ❌ Bad: Strategy with shared mutable state2class BadStrategy:3 shared_cache = {} # Class-level shared state!4
5 def execute(self, data):6 self.shared_cache[data] = result # Bad: Mutating shared state7 return result8
9# ✅ Good: Strategy without shared state10class GoodStrategy:11 def __init__(self):12 self._cache = {} # Instance-level state13
14 def execute(self, data):15 if data not in self._cache:16 self._cache[data] = self._compute(data)17 return self._cache[data]1// ❌ Bad: Strategy with shared mutable state2class BadStrategy implements Strategy {3 private static Map<String, Object> sharedCache = new HashMap<>(); // Shared state!4
5 @Override6 public Object execute(String data) {7 sharedCache.put(data, result); // Bad: Mutating shared state8 return result;9 }10}11
12// ✅ Good: Strategy without shared state13class GoodStrategy implements Strategy {14 private Map<String, Object> cache = new HashMap<>(); // Instance-level15
16 @Override17 public Object execute(String data) {18 if (!cache.containsKey(data)) {19 cache.put(data, compute(data));20 }21 return cache.get(data);22 }23}Mistake 2: Context Exposing Strategy Details
Section titled “Mistake 2: Context Exposing Strategy Details”1# ❌ Bad: Context exposes strategy internals2class BadContext:3 def __init__(self, strategy):4 self.strategy = strategy # Public access!5
6 def get_strategy_name(self): # Bad: Exposing strategy details7 return self.strategy.__class__.__name__8
9# ✅ Good: Context hides strategy details10class GoodContext:11 def __init__(self, strategy):12 self._strategy = strategy # Private13
14 def execute(self, data):15 return self._strategy.execute(data)16 # No methods exposing strategy internals1// ❌ Bad: Context exposes strategy internals2class BadContext {3 public Strategy strategy; // Public access!4
5 public String getStrategyName() { // Bad: Exposing strategy details6 return strategy.getClass().getSimpleName();7 }8}9
10// ✅ Good: Context hides strategy details11class GoodContext {12 private Strategy strategy; // Private13
14 public GoodContext(Strategy strategy) {15 this.strategy = strategy;16 }17
18 public Object execute(String data) {19 return strategy.execute(data);20 }21 // No methods exposing strategy internals22}Mistake 3: Strategy Knowing About Context
Section titled “Mistake 3: Strategy Knowing About Context”1# ❌ Bad: Strategy depends on Context2class BadStrategy:3 def __init__(self, context): # Bad: Strategy knows about context4 self.context = context5
6 def execute(self, data):7 return self.context.some_method() # Bad: Tight coupling!8
9# ✅ Good: Strategy is independent10class GoodStrategy:11 def execute(self, data, helper_func=None):12 # Strategy doesn't know about context13 # If needed, pass data through parameters14 if helper_func:15 return helper_func(data)16 return self._process(data)1// ❌ Bad: Strategy depends on Context2class BadStrategy implements Strategy {3 private Context context; // Bad: Strategy knows about context4
5 public BadStrategy(Context context) {6 this.context = context;7 }8
9 @Override10 public Object execute(String data) {11 return context.someMethod(); // Bad: Tight coupling!12 }13}14
15// ✅ Good: Strategy is independent16class GoodStrategy implements Strategy {17 @Override18 public Object execute(String data) {19 // Strategy doesn't know about context20 return process(data);21 }22}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”1Context → 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”1class Strategy(ABC):2 @abstractmethod3 def execute(self, data): pass4
5class ConcreteStrategy(Strategy):6 def execute(self, data):7 return process(data)8
9class Context:10 def __init__(self, strategy: Strategy):11 self._strategy = strategy12
13 def set_strategy(self, strategy: Strategy):14 self._strategy = strategy15
16 def do_work(self, data):17 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# 1. Strategy Interface2class Strategy(ABC):3 @abstractmethod4 def execute(self, data): pass5
6# 2. Concrete Strategies7class StrategyA(Strategy):8 def execute(self, data):9 return process_a(data)10
11class StrategyB(Strategy):12 def execute(self, data):13 return process_b(data)14
15# 3. Context16class Context:17 def __init__(self, strategy: Strategy):18 self._strategy = strategy19
20 def set_strategy(self, strategy: Strategy):21 self._strategy = strategy22
23 def execute(self, data):24 return self._strategy.execute(data)25
26# 4. Usage27context = Context(StrategyA())28context.execute(data)29context.set_strategy(StrategyB())30context.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! 🔄