How to Implement the Strategy Pattern in Python
This Python code demonstrates the Strategy design pattern using interchangeable sorting algorithms (bubble sort and quick sort) that can be swapped at runtime.
Python code
39 linesclass SortingStrategy:
def sort(self, data):
raise NotImplementedError
class BubbleSort(SortingStrategy):
def sort(self, data):
result = data.copy()
n = len(result)
for i in range(n):
for j in range(0, n - i - 1):
if result[j] > result[j + 1]:
result[j], result[j + 1] = result[j + 1], result[j]
return result
class QuickSort(SortingStrategy):
def sort(self, data):
if len(data) <= 1:
return data.copy()
pivot = data[0]
less = [x for x in data[1:] if x <= pivot]
greater = [x for x in data[1:] if x > pivot]
return self.sort(less) + [pivot] + self.sort(greater)
class Sorter:
def __init__(self, strategy):
self._strategy = strategy
def set_strategy(self, strategy):
self._strategy = strategy
def execute(self, data):
return self._strategy.sort(data)
if __name__ == "__main__":
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorter = Sorter(BubbleSort())
print("Bubble:", sorter.execute(numbers))
sorter.set_strategy(QuickSort())
print("Quick:", sorter.execute(numbers))
Output
Bubble: [1, 1, 2, 3, 4, 5, 6, 9]
Quick: [1, 1, 2, 3, 4, 5, 6, 9]
How it works
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime. Here, SortingStrategy is an abstract base class with a sort method that subclasses must implement. BubbleSort and QuickSort provide concrete algorithms by overriding sort. The Sorter class holds a reference to a strategy and delegates the sorting task to it, allowing clients to switch algorithms without modifying the Sorter code. This separation of concerns promotes code reuse and makes the system flexible and easier to maintain.
Common mistakes
- Not creating a common interface for strategies, leading to code that checks class types explicitly.
- Reusing a list reference instead of copying, causing the original data to be modified unexpectedly.
- Forgetting to handle base cases in recursive algorithms like quicksort, leading to infinite recursion or errors.
Variations
- Use `functools.singledispatch` to implement similar behavior with function overloading.
- Make strategy selection based on data size or constraints dynamically in the sorter.
Real-world use cases
- Choosing between different compression algorithms (e.g., gzip vs. bzip2) at runtime in backup and file archival tools.
- Selecting a payment gateway strategy (e.g., credit card, PayPal, crypto) in e-commerce applications based on user preference.
- Implementing different authentication methods (e.g., OAuth, JWT, API key) that are interchangeable in an API security layer.
Sponsored
More from System design patterns
- Build a BFF (Backend for Frontend) Mock Aggregator in Python medium
- Builder pattern for mocking complex objects in Python easy
- Circuit Breaker Pattern in Python: Closed, Open, and Half-Open States medium
- Create a Data Helper Class in Python easy
- Domain Driven Design Aggregate Root Example in Python medium
- Facade Pattern in Python with Mock Simplification medium
Keep learning
Related tutorials and quizzes for this topic.