Skip to content

Strategy Pattern — Interchangeable Algorithms at Runtime

DodaTech Updated 2026-06-24 4 min read

What You'll Learn

You will learn how the Strategy Pattern enables selecting an algorithm at runtime by encapsulating each algorithm in its own class, promoting Composition Over Inheritance. You will distinguish Strategy from State and Template Method.

Why It Matters

An e-commerce site calculates shipping costs differently for standard, Express, and international delivery. Embedding all three algorithms in the order class violates the Single Responsibility and Open/Closed Principles. Strategy extracts each shipping method into a separate, swappable component. The result is a system where new shipping methods can be added (FedEx, DHL, drone delivery) without touching existing code — just add a new Strategy class and configure it.

Real-World Use

Sorting Algorithms — you might use quicksort for small arrays, merge sort for stable sorting, and timsort for partially sorted data — all selectable through a common Comparator interface. DodaTech's compression module selects between gzip, brotli, and zstd strategies based on file type and size, allowing users to configure compression levels per content type without changing the compression pipeline code.

The Pattern

Context maintains a reference to a Strategy. Strategy declares the algorithm interface. ConcreteStrategies implement specific algorithms.

from abc import ABC, abstractmethod

class ShippingStrategy(ABC):
    @abstractmethod
    def calculate(self, weight_kg: float, distance_km: float) -> float:
        pass

class StandardShipping(ShippingStrategy):
    def calculate(self, weight_kg: float, distance_km: float) -> float:
        return weight_kg * 0.5 + distance_km * 0.01

class ExpressShipping(ShippingStrategy):
    def calculate(self, weight_kg: float, distance_km: float) -> float:
        return weight_kg * 1.0 + distance_km * 0.05 + 10.0

class InternationalShipping(ShippingStrategy):
    def calculate(self, weight_kg: float, distance_km: float) -> float:
        return weight_kg * 2.0 + distance_km * 0.1 + 15.0

class ShippingCalculator:
    def __init__(self, strategy: ShippingStrategy):
        self._strategy = strategy

    def set_strategy(self, strategy: ShippingStrategy):
        self._strategy = strategy

    def calculate_cost(self, weight_kg: float, distance_km: float) -> float:
        return self._strategy.calculate(weight_kg, distance_km)
calculator = ShippingCalculator(StandardShipping())
weight, distance = 5.0, 200.0

for name, strategy in [("Standard", StandardShipping()), ("Express", ExpressShipping()), ("International", InternationalShipping())]:
    calculator.set_strategy(strategy)
    cost = calculator.calculate_cost(weight, distance)
    print(f"{name}: ${cost:.2f}")
Standard: $4.50
Express: $20.00
International: $45.00

Structure

classDiagram
    class Context {
        -strategy: Strategy
        +setStrategy(Strategy)
        +executeStrategy()
    }
    class Strategy {
        <>
        +algorithm()
    }
    class ConcreteStrategyA {
        +algorithm()
    }
    class ConcreteStrategyB {
        +algorithm()
    }
    class ConcreteStrategyC {
        +algorithm()
    }
    Context --> Strategy : delegates
    ConcreteStrategyA ..|> Strategy
    ConcreteStrategyB ..|> Strategy
    ConcreteStrategyC ..|> Strategy

Real-World Usage

  • Java Comparator interface — sorting strategies passed to Collections.sort().
  • Python sorted() key function — a function-based Strategy for custom sort ordering.
  • HTTP authentication mechanisms — Basic, Bearer, Digest, OAuth — all interchangeable through a common Authenticator interface.
  • Compression algorithms in tar--gzip, --bzip2, --xz flags select different compression strategies.
  • State shares the same structure but transitions itself; Strategy is set by the client.
  • Template Method defines an algorithm skeleton and lets subclasses override steps; Strategy lets you swap entire algorithms.
  • Decorator adds behaviour; Strategy replaces behaviour.
  • Flyweight can make Strategy instances reusable across contexts.

Pros and Cons

Pros Cons
Algorithms are isolated and independently testable Clients must be aware of different strategies
Easy to add new strategies without changing context Increases number of classes
Eliminates conditional statements for algorithm selection Strategy creation and configuration overhead
Composition Over Inheritance — more flexible Not all algorithms fit the same interface cleanly

The key insight in the code is that ShippingCalculator holds a reference to a ShippingStrategy and delegates the calculate call. The calculator doesn't know which concrete Strategy it's using — it only knows the interface. This makes the strategies completely interchangeable at runtime, as demonstrated by the loop that swaps strategies and recalculates the same shipment.

Practice Questions

  1. Implement a payment processing system with CreditCard, PayPal, and Crypto strategies.
  2. How would you allow the client to configure a Strategy with parameters (e.g., API keys for payment gateways)?
  3. Compare Strategy with lambdas/first-class functions — when is a full class necessary versus a simple callback?
  4. Implement a Strategy selector that chooses the best Strategy based on input data characteristics (e.g., selecting compression Strategy based on file type).

Challenge

Implement a Strategy that decorates another Strategy with logging or timing. For example, a TimedShipping Strategy that wraps any shipping Strategy and logs the calculation time.

Real-World Task

Identify a method in your codebase that contains a large if-else chain selecting between algorithms or behaviours. Refactor it using the Strategy Pattern and use DodaTech's A/B testing framework to compare the performance of different strategies in production.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro