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. You can select an algorithm at compile time through templates, or at runtime through virtual functions or function pointers. Reach for it when one class keeps growing a switch over algorithms.

The problem

You have a class that performs an operation in multiple ways. The naive approach hardcodes all variants using conditional logic:

class DataProcessor {
public:
    void process(Data& data, const std::string& algorithm) {
        if (algorithm == "quick_sort") {
            // quick sort implementation
        } else if (algorithm == "merge_sort") {
            // merge sort implementation
        } else if (algorithm == "heap_sort") {
            // heap sort implementation
        }
    }
};

This approach has several problems:

  • Tight coupling. The processor knows about every algorithm.
  • Hard to extend. Adding an algorithm means modifying the processor class.
  • Single Responsibility. One class handles multiple concerns.
  • Open/Closed Principle. The class is open for extension only through modification.
  • Maintainability. The class grows more complex with every algorithm.

The Strategy pattern fixes this by extracting algorithms into separate, interchangeable objects.

Real-world analogy

Think of getting to the airport:

  • Goal: get to the airport (the operation)
  • Strategies: drive, take a taxi, use public transit, or cycle
  • Flexibility: you pick a strategy based on time, cost, and weather
  • Interchangeability: each strategy gets you there, and you can switch without changing your travel plan
  • Encapsulation: each strategy handles its own details, such as traffic routes or ticket purchases

Key concepts

Two implementation approaches

The Strategy pattern has two main implementation styles:

  1. Template-Based (Compile-Time). Algorithms are selected through C++ templates, and the type is known at compile time.
  2. Runtime Selection (Dynamic). Algorithms are selected at runtime through virtual functions or function pointers, and the type is determined at execution time.

Encapsulation levels

  • Algorithm encapsulation: each algorithm is a separate class or template
  • Context independence: the client using a strategy does not need to know about other strategies
  • Behavior variation: different strategies can have completely different internal interfaces

Trade-offs

Aspect Benefit Cost
Algorithm isolation Each algorithm is independent and testable More classes/code structures
Runtime flexibility Switch algorithms at runtime (dynamic approach) Virtual function overhead, pointer indirection
Compile-time safety Type-safe, zero runtime overhead (template approach) Algorithm must be known at compile-time, template code duplication
Extensibility Adding algorithms doesn't modify existing code Requires creating new strategy classes
Testability Algorithms can be tested in isolation More complex object graphs to set up

When to use strategy

Good use cases:

  • Multiple algorithms for the same task, such as sorting, compression, or serialization
  • Algorithm selection depends on runtime data or configuration
  • You need to switch algorithms dynamically based on performance or behavior requirements
  • You want to avoid conditional logic in client code
  • Testing algorithms in isolation matters

Consider the template approach when:

  • The algorithm is determined at compile time
  • You need a zero-cost abstraction with no virtual function overhead
  • Performance is critical
  • All code paths are known at compile time

Consider runtime selection when:

  • The algorithm must be chosen at runtime
  • Configuration files or environment variables determine the strategy
  • User input determines which algorithm to use
  • You have a plugin architecture where strategies are loaded dynamically

Avoid it when:

  • Only one algorithm exists
  • Algorithms rarely change
  • The context is simple and conditional logic is clearer than the abstraction

Related patterns

  • State: similar structure, but it changes object behavior based on internal state
  • Decorator: also wraps behavior, but adds functionality. Strategy swaps entire algorithms.
  • Factory: often used with Strategy to instantiate the right strategy
  • Template Method: the opposite approach. The base class defines the algorithm structure and subclasses fill in the steps.

Implementation styles

Which style you use depends on your needs:

  • Policy Based Design. An advanced C++ technique using templates, where strategies are template parameters (traits or policies).
  • Template-Based Selection. Compile-time strategy selection through template specialization.
  • Runtime Selection. Virtual functions or function pointers for runtime strategy selection.

Summary

Use Strategy when a class has a growing switch over algorithms. Templates win when the algorithm is fixed at compile time. Virtual functions win when configuration or user input picks it. Policy Based Design goes further, but it fits library code that can afford the compile-time cost.