Strategy Pattern

The Strategy pattern defines a family of interchangeable algorithms or behaviors, encapsulates each one, and allows them to be swapped at runtime without modifying the calling code.

This pattern is useful when multiple approaches exist to perform a task, and you want to select the appropriate one dynamically, promoting cleaner logic and improved maintainability.

This pattern should be implemented manually: it does not require built-in helpers from pattern_kit.

Use Cases

  • Switching between different sorting or filtering strategies

  • Applying different pricing, tax, or discount rules

  • Customizing logging, serialization, or validation logic

  • Pluggable behavior in UI elements or background tasks

  • AI/game agents choosing a decision-making strategy

Example

from abc import ABC, abstractmethod

class DiscountStrategy(ABC):
    @abstractmethod
    def apply(self, price: float) -> float:
        pass

class NoDiscount(DiscountStrategy):
    def apply(self, price):
        return price

class TenPercentOff(DiscountStrategy):
    def apply(self, price):
        return price * 0.9

# Usage
def calculate_total(price: float, strategy: DiscountStrategy) -> float:
    return strategy.apply(price)

print(calculate_total(100, NoDiscount()))       # 100.0
print(calculate_total(100, TenPercentOff()))    # 90.0

Benefits

  • Eliminates long if/else blocks

  • Makes algorithms testable in isolation

  • Allows behavior to change at runtime

  • Encourages composition over inheritance