Strategy Pattern

The Strategy is a behavioral design pattern that enables selecting an algorithm at runtime by encapsulating each one as a separate interchangeable class. It lets you define a family of algorithms, make them interchangeable, and vary them independently from the client that uses them.

Key Principles

Structure

Python Example: Sorting Strategies

from abc import ABC, abstractmethod
from typing import List

# Strategy Interface
class SortStrategy(ABC):
    @abstractmethod
    def sort(self, data: List[int]) -> List[int]:
        pass

# Concrete Strategies
class BubbleSort(SortStrategy):
    def sort(self, data: List[int]) -> List[int]:
        n = len(data)
        for i in range(n):
            for j in range(0, n-i-1):
                if data[j] > data[j+1]:
                    data[j], data[j+1] = data[j+1], data[j]
        return data

class QuickSort(SortStrategy):
    def sort(self, data: List[int]) -> List[int]:
        if len(data) <= 1:
            return data
        pivot = data[len(data)//2]
        left = [x for x in data if x < pivot]
        middle = [x for x in data if x == pivot]
        right = [x for x in data if x > pivot]
        return self.sort(left) + middle + self.sort(right)

class MergeSort(SortStrategy):
    def sort(self, data: List[int]) -> List[int]:
        if len(data) <= 1:
            return data
        mid = len(data) // 2
        left = self.sort(data[:mid])
        right = self.sort(data[mid:])
        return self._merge(left, right)

    def _merge(self, left, right):
        result = []
        i = j = 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                result.append(left[i])
                i += 1
            else:
                result.append(right[j])
                j += 1
        result.extend(left[i:])
        result.extend(right[j:])
        return result

# Context
class Sorter:
    def __init__(self, strategy: SortStrategy):
        self.strategy = strategy

    def set_strategy(self, strategy: SortStrategy):
        self.strategy = strategy

    def sort_data(self, data: List[int]) -> List[int]:
        print(f"Using {self.strategy.__class__.__name__}")
        return self.strategy.sort(data.copy())  # Return sorted copy

# Usage (Client Code)
if __name__ == "__main__":
    data = [64, 34, 25, 12, 22, 11, 90]

    sorter = Sorter(BubbleSort())
    print(sorter.sort_data(data))  # Bubble sort

    sorter.set_strategy(QuickSort())
    print(sorter.sort_data(data))  # Quick sort

    sorter.set_strategy(MergeSort())
    print(sorter.sort_data(data))  # Merge sort

Output:

Using BubbleSort
[11, 12, 22, 25, 34, 64, 90]
Using QuickSort
[11, 12, 22, 25, 34, 64, 90]
Using MergeSort
[11, 12, 22, 25, 34, 64, 90]

When to Use Strategy

Summary: Strategy defines a family of interchangeable algorithms and lets the context use any one at runtime via composition, eliminating conditional sprawl.