Skip to content

Strategy Pattern

Category: Behavioral Pattern

Overview

Define a family of algorithms, encapsulate each one, and make them interchangeable. This pattern lets algorithms vary independently from clients that use them, enabling runtime selection of behavior without complex conditional logic.

Usage Guidelines

Use when:

  • Multiple ways to perform an operation need to be chosen at runtime
  • Code contains many conditionals that select behavior variants
  • Related algorithms share a common interface but differ in implementation
  • Algorithm implementation details should be isolated from client code

Avoid when:

  • Only one way to perform the operation exists
  • The algorithm is trivial and doesn't justify abstraction
  • The behavior never changes or has no variants
  • The indirection overhead is unacceptable for performance

Implementation

from abc import ABC, abstractmethod

# Strategy Interface
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount: float) -> str:
        pass

# Concrete Strategies
class CreditCardPayment(PaymentStrategy):
    def __init__(self, card_number: str):
        self.card_number = card_number

    def pay(self, amount: float) -> str:
        return f"Paid ${amount:.2f} using Credit Card ending in {self.card_number[-4:]}"

class PayPalPayment(PaymentStrategy):
    def __init__(self, email: str):
        self.email = email

    def pay(self, amount: float) -> str:
        return f"Paid ${amount:.2f} using PayPal account {self.email}"

class CryptocurrencyPayment(PaymentStrategy):
    def __init__(self, wallet_address: str):
        self.wallet_address = wallet_address

    def pay(self, amount: float) -> str:
        return f"Paid ${amount:.2f} using Crypto wallet {self.wallet_address[:10]}..."

# Context
class ShoppingCart:
    def __init__(self):
        self._items: list[float] = []
        self._payment_strategy: PaymentStrategy | None = None

    def add_item(self, price: float) -> None:
        self._items.append(price)

    def get_total(self) -> float:
        return sum(self._items)

    def set_payment_strategy(self, strategy: PaymentStrategy) -> None:
        self._payment_strategy = strategy

    def checkout(self) -> str:
        if self._payment_strategy is None:
            raise ValueError("Payment strategy not set")

        total = self.get_total()
        if total == 0:
            return "Cart is empty"

        return self._payment_strategy.pay(total)

Usage

# Create shopping cart
cart = ShoppingCart()
cart.add_item(100.00)
cart.add_item(50.00)

# Pay with credit card
cart.set_payment_strategy(CreditCardPayment("1234-5678-9012-3456"))
print(cart.checkout())  # Paid $150.00 using Credit Card ending in 3456

# Change strategy to PayPal
cart.set_payment_strategy(PayPalPayment("user@example.com"))
print(cart.checkout())  # Paid $150.00 using PayPal account user@example.com

# Change strategy to Cryptocurrency
cart.set_payment_strategy(CryptocurrencyPayment("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"))
print(cart.checkout())  # Paid $150.00 using Crypto wallet 0x742d35Cc...

Trade-offs

Benefits:

  1. Algorithms can be switched at runtime for flexibility
  2. New strategies can be added without modifying context (Open/Closed Principle)
  3. Eliminates complex conditional logic through clean object composition
  4. Each strategy can be tested independently

Drawbacks:

  1. Creates many strategy objects increasing class count
  2. Clients must understand different strategies to select appropriately
  3. Context and strategy must share data with communication overhead
  4. Overkill for simple algorithms that rarely change

Real-World Examples

  • Sorting algorithms choosing between bubble sort, quick sort, merge sort
  • Compression algorithm selection (ZIP, RAR, TAR)
  • Route planning with different strategies (shortest, fastest, scenic)
  • Authentication methods (OAuth, JWT, Basic Auth)
  • State
  • Template Method
  • Command
  • Factory

API Reference

design_patterns.behavioral.strategy

Strategy Pattern Module

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it. This pattern is useful when you have multiple ways to perform an operation and want to choose the appropriate one at runtime.

Example

Using different payment strategies:

cart = ShoppingCart()
cart.add_item(100)
cart.add_item(50)

# Pay with credit card
cart.set_payment_strategy(CreditCardPayment("1234-5678-9012-3456"))
cart.checkout()

# Pay with PayPal
cart.set_payment_strategy(PayPalPayment("user@example.com"))
cart.checkout()

BubbleSort

Bases: SortStrategy

Bubble sort algorithm strategy.

Source code in src/design_patterns/behavioral/strategy.py
class BubbleSort(SortStrategy):
    """Bubble sort algorithm strategy."""

    def sort(self, data: list[int]) -> list[int]:
        """Sort using bubble sort.

        Args:
            data: The list to sort.

        Returns:
            The sorted list.
        """
        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

sort(data)

Sort using bubble sort.

Parameters:

Name Type Description Default
data list[int]

The list to sort.

required

Returns:

Type Description
list[int]

The sorted list.

Source code in src/design_patterns/behavioral/strategy.py
def sort(self, data: list[int]) -> list[int]:
    """Sort using bubble sort.

    Args:
        data: The list to sort.

    Returns:
        The sorted list.
    """
    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

CreditCardPayment

Bases: PaymentStrategy

Payment strategy using credit card.

Source code in src/design_patterns/behavioral/strategy.py
class CreditCardPayment(PaymentStrategy):
    """Payment strategy using credit card."""

    def __init__(self, card_number: str) -> None:
        """Initialize credit card payment.

        Args:
            card_number: The credit card number.
        """
        self.card_number = card_number

    def pay(self, amount: float) -> str:
        """Process credit card payment.

        Args:
            amount: The amount to pay.

        Returns:
            Payment confirmation message.
        """
        return f"Paid ${amount:.2f} using Credit Card ending in {self.card_number[-4:]}"

__init__(card_number)

Initialize credit card payment.

Parameters:

Name Type Description Default
card_number str

The credit card number.

required
Source code in src/design_patterns/behavioral/strategy.py
def __init__(self, card_number: str) -> None:
    """Initialize credit card payment.

    Args:
        card_number: The credit card number.
    """
    self.card_number = card_number

pay(amount)

Process credit card payment.

Parameters:

Name Type Description Default
amount float

The amount to pay.

required

Returns:

Type Description
str

Payment confirmation message.

Source code in src/design_patterns/behavioral/strategy.py
def pay(self, amount: float) -> str:
    """Process credit card payment.

    Args:
        amount: The amount to pay.

    Returns:
        Payment confirmation message.
    """
    return f"Paid ${amount:.2f} using Credit Card ending in {self.card_number[-4:]}"

CryptocurrencyPayment

Bases: PaymentStrategy

Payment strategy using cryptocurrency.

Source code in src/design_patterns/behavioral/strategy.py
class CryptocurrencyPayment(PaymentStrategy):
    """Payment strategy using cryptocurrency."""

    def __init__(self, wallet_address: str) -> None:
        """Initialize cryptocurrency payment.

        Args:
            wallet_address: The cryptocurrency wallet address.
        """
        self.wallet_address = wallet_address

    def pay(self, amount: float) -> str:
        """Process cryptocurrency payment.

        Args:
            amount: The amount to pay.

        Returns:
            Payment confirmation message.
        """
        return f"Paid ${amount:.2f} using Crypto wallet {self.wallet_address[:10]}..."

__init__(wallet_address)

Initialize cryptocurrency payment.

Parameters:

Name Type Description Default
wallet_address str

The cryptocurrency wallet address.

required
Source code in src/design_patterns/behavioral/strategy.py
def __init__(self, wallet_address: str) -> None:
    """Initialize cryptocurrency payment.

    Args:
        wallet_address: The cryptocurrency wallet address.
    """
    self.wallet_address = wallet_address

pay(amount)

Process cryptocurrency payment.

Parameters:

Name Type Description Default
amount float

The amount to pay.

required

Returns:

Type Description
str

Payment confirmation message.

Source code in src/design_patterns/behavioral/strategy.py
def pay(self, amount: float) -> str:
    """Process cryptocurrency payment.

    Args:
        amount: The amount to pay.

    Returns:
        Payment confirmation message.
    """
    return f"Paid ${amount:.2f} using Crypto wallet {self.wallet_address[:10]}..."

DataSorter

Context class that uses different sorting strategies.

Source code in src/design_patterns/behavioral/strategy.py
class DataSorter:
    """Context class that uses different sorting strategies."""

    def __init__(self, strategy: SortStrategy) -> None:
        """Initialize with a sorting strategy.

        Args:
            strategy: The sorting strategy to use.
        """
        self._strategy = strategy

    def set_strategy(self, strategy: SortStrategy) -> None:
        """Change the sorting strategy.

        Args:
            strategy: The new sorting strategy.
        """
        self._strategy = strategy

    def sort_data(self, data: list[int]) -> list[int]:
        """Sort data using the current strategy.

        Args:
            data: The list to sort.

        Returns:
            The sorted list.
        """
        return self._strategy.sort(data)

__init__(strategy)

Initialize with a sorting strategy.

Parameters:

Name Type Description Default
strategy SortStrategy

The sorting strategy to use.

required
Source code in src/design_patterns/behavioral/strategy.py
def __init__(self, strategy: SortStrategy) -> None:
    """Initialize with a sorting strategy.

    Args:
        strategy: The sorting strategy to use.
    """
    self._strategy = strategy

set_strategy(strategy)

Change the sorting strategy.

Parameters:

Name Type Description Default
strategy SortStrategy

The new sorting strategy.

required
Source code in src/design_patterns/behavioral/strategy.py
def set_strategy(self, strategy: SortStrategy) -> None:
    """Change the sorting strategy.

    Args:
        strategy: The new sorting strategy.
    """
    self._strategy = strategy

sort_data(data)

Sort data using the current strategy.

Parameters:

Name Type Description Default
data list[int]

The list to sort.

required

Returns:

Type Description
list[int]

The sorted list.

Source code in src/design_patterns/behavioral/strategy.py
def sort_data(self, data: list[int]) -> list[int]:
    """Sort data using the current strategy.

    Args:
        data: The list to sort.

    Returns:
        The sorted list.
    """
    return self._strategy.sort(data)

MergeSort

Bases: SortStrategy

Merge sort algorithm strategy.

Source code in src/design_patterns/behavioral/strategy.py
class MergeSort(SortStrategy):
    """Merge sort algorithm strategy."""

    def sort(self, data: list[int]) -> list[int]:
        """Sort using merge sort.

        Args:
            data: The list to sort.

        Returns:
            The sorted list.
        """
        if len(data) <= 1:
            return data.copy()

        mid = len(data) // 2
        left = self.sort(data[:mid])
        right = self.sort(data[mid:])

        return self._merge(left, right)

    def _merge(self, left: list[int], right: list[int]) -> list[int]:
        """Merge two sorted lists.

        Args:
            left: First sorted list.
            right: Second sorted list.

        Returns:
            Merged sorted list.
        """
        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

sort(data)

Sort using merge sort.

Parameters:

Name Type Description Default
data list[int]

The list to sort.

required

Returns:

Type Description
list[int]

The sorted list.

Source code in src/design_patterns/behavioral/strategy.py
def sort(self, data: list[int]) -> list[int]:
    """Sort using merge sort.

    Args:
        data: The list to sort.

    Returns:
        The sorted list.
    """
    if len(data) <= 1:
        return data.copy()

    mid = len(data) // 2
    left = self.sort(data[:mid])
    right = self.sort(data[mid:])

    return self._merge(left, right)

PayPalPayment

Bases: PaymentStrategy

Payment strategy using PayPal.

Source code in src/design_patterns/behavioral/strategy.py
class PayPalPayment(PaymentStrategy):
    """Payment strategy using PayPal."""

    def __init__(self, email: str) -> None:
        """Initialize PayPal payment.

        Args:
            email: The PayPal account email.
        """
        self.email = email

    def pay(self, amount: float) -> str:
        """Process PayPal payment.

        Args:
            amount: The amount to pay.

        Returns:
            Payment confirmation message.
        """
        return f"Paid ${amount:.2f} using PayPal account {self.email}"

__init__(email)

Initialize PayPal payment.

Parameters:

Name Type Description Default
email str

The PayPal account email.

required
Source code in src/design_patterns/behavioral/strategy.py
def __init__(self, email: str) -> None:
    """Initialize PayPal payment.

    Args:
        email: The PayPal account email.
    """
    self.email = email

pay(amount)

Process PayPal payment.

Parameters:

Name Type Description Default
amount float

The amount to pay.

required

Returns:

Type Description
str

Payment confirmation message.

Source code in src/design_patterns/behavioral/strategy.py
def pay(self, amount: float) -> str:
    """Process PayPal payment.

    Args:
        amount: The amount to pay.

    Returns:
        Payment confirmation message.
    """
    return f"Paid ${amount:.2f} using PayPal account {self.email}"

PaymentStrategy

Bases: ABC

Abstract base class for payment strategies.

Source code in src/design_patterns/behavioral/strategy.py
class PaymentStrategy(ABC):
    """Abstract base class for payment strategies."""

    @abstractmethod
    def pay(self, amount: float) -> str:
        """Process a payment.

        Args:
            amount: The amount to pay.

        Returns:
            A message indicating payment status.
        """
        pass

pay(amount) abstractmethod

Process a payment.

Parameters:

Name Type Description Default
amount float

The amount to pay.

required

Returns:

Type Description
str

A message indicating payment status.

Source code in src/design_patterns/behavioral/strategy.py
@abstractmethod
def pay(self, amount: float) -> str:
    """Process a payment.

    Args:
        amount: The amount to pay.

    Returns:
        A message indicating payment status.
    """
    pass

QuickSort

Bases: SortStrategy

Quick sort algorithm strategy.

Source code in src/design_patterns/behavioral/strategy.py
class QuickSort(SortStrategy):
    """Quick sort algorithm strategy."""

    def sort(self, data: list[int]) -> list[int]:
        """Sort using quick sort.

        Args:
            data: The list to sort.

        Returns:
            The sorted list.
        """
        if len(data) <= 1:
            return data.copy()

        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)

sort(data)

Sort using quick sort.

Parameters:

Name Type Description Default
data list[int]

The list to sort.

required

Returns:

Type Description
list[int]

The sorted list.

Source code in src/design_patterns/behavioral/strategy.py
def sort(self, data: list[int]) -> list[int]:
    """Sort using quick sort.

    Args:
        data: The list to sort.

    Returns:
        The sorted list.
    """
    if len(data) <= 1:
        return data.copy()

    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)

ShoppingCart

Shopping cart that uses different payment strategies.

Source code in src/design_patterns/behavioral/strategy.py
class ShoppingCart:
    """Shopping cart that uses different payment strategies."""

    def __init__(self) -> None:
        """Initialize an empty shopping cart."""
        self._items: list[float] = []
        self._payment_strategy: PaymentStrategy | None = None

    def add_item(self, price: float) -> None:
        """Add an item to the cart.

        Args:
            price: The price of the item.
        """
        self._items.append(price)

    def get_total(self) -> float:
        """Calculate the total price of all items.

        Returns:
            The total price.
        """
        return sum(self._items)

    def set_payment_strategy(self, strategy: PaymentStrategy) -> None:
        """Set the payment strategy.

        Args:
            strategy: The payment strategy to use.
        """
        self._payment_strategy = strategy

    def checkout(self) -> str:
        """Process the payment using the selected strategy.

        Returns:
            Payment confirmation message.

        Raises:
            ValueError: If no payment strategy is set.
        """
        if self._payment_strategy is None:
            raise ValueError("Payment strategy not set")

        total = self.get_total()
        if total == 0:
            return "Cart is empty"

        return self._payment_strategy.pay(total)

__init__()

Initialize an empty shopping cart.

Source code in src/design_patterns/behavioral/strategy.py
def __init__(self) -> None:
    """Initialize an empty shopping cart."""
    self._items: list[float] = []
    self._payment_strategy: PaymentStrategy | None = None

add_item(price)

Add an item to the cart.

Parameters:

Name Type Description Default
price float

The price of the item.

required
Source code in src/design_patterns/behavioral/strategy.py
def add_item(self, price: float) -> None:
    """Add an item to the cart.

    Args:
        price: The price of the item.
    """
    self._items.append(price)

checkout()

Process the payment using the selected strategy.

Returns:

Type Description
str

Payment confirmation message.

Raises:

Type Description
ValueError

If no payment strategy is set.

Source code in src/design_patterns/behavioral/strategy.py
def checkout(self) -> str:
    """Process the payment using the selected strategy.

    Returns:
        Payment confirmation message.

    Raises:
        ValueError: If no payment strategy is set.
    """
    if self._payment_strategy is None:
        raise ValueError("Payment strategy not set")

    total = self.get_total()
    if total == 0:
        return "Cart is empty"

    return self._payment_strategy.pay(total)

get_total()

Calculate the total price of all items.

Returns:

Type Description
float

The total price.

Source code in src/design_patterns/behavioral/strategy.py
def get_total(self) -> float:
    """Calculate the total price of all items.

    Returns:
        The total price.
    """
    return sum(self._items)

set_payment_strategy(strategy)

Set the payment strategy.

Parameters:

Name Type Description Default
strategy PaymentStrategy

The payment strategy to use.

required
Source code in src/design_patterns/behavioral/strategy.py
def set_payment_strategy(self, strategy: PaymentStrategy) -> None:
    """Set the payment strategy.

    Args:
        strategy: The payment strategy to use.
    """
    self._payment_strategy = strategy

SortStrategy

Bases: ABC

Abstract base class for sorting strategies.

Source code in src/design_patterns/behavioral/strategy.py
class SortStrategy(ABC):
    """Abstract base class for sorting strategies."""

    @abstractmethod
    def sort(self, data: list[int]) -> list[int]:
        """Sort the given data.

        Args:
            data: The list of integers to sort.

        Returns:
            The sorted list.
        """
        pass

sort(data) abstractmethod

Sort the given data.

Parameters:

Name Type Description Default
data list[int]

The list of integers to sort.

required

Returns:

Type Description
list[int]

The sorted list.

Source code in src/design_patterns/behavioral/strategy.py
@abstractmethod
def sort(self, data: list[int]) -> list[int]:
    """Sort the given data.

    Args:
        data: The list of integers to sort.

    Returns:
        The sorted list.
    """
    pass