← All Patterns
behavioral4 participants

Strategy

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

The Problem

Your class does the same job multiple ways depending on context — different sort orders, different sync mechanisms, different payment providers. Using if/else chains or subclasses for each combination leads to explosion of code paths and makes adding new variants painful.

Structure

Contextstrategy_CoarseGrainedFineGrainedAtomic«interface»StrategyCoarseGrainedexecute()FineGrainedexecute()Atomicexecute()implements

Execution Walkthrough

1

Define interface

Create a Strategy interface with the single method the Context will call (e.g. lock/unlock, execute, compress).

2

Concrete impls

3

Context stores

4

Inject at runtime

5

Delegate call

Code Comparison

Every new sync mode requires modifying SimEngine. Adding Atomic locking means touching the engine itself.

// BAD: algorithm baked into the context
class SimEngine {
  SyncMode mode_;
public:
  void run() {
    if (mode_ == COARSE) {
      global_mutex.lock();
      // ... run simulation ...
      global_mutex.unlock();
    } else if (mode_ == FINE) {
      // ... different locking ...
    } else if (mode_ == ATOMIC) {
      // ... yet another variant ...
    }
    // Add new mode? Modify this class.
  }
};

Full C++ Implementation

C++ · Strategy
#include <mutex>
#include <shared_mutex>
#include <atomic>
#include <memory>
#include <iostream>

struct SyncStrategy {
  virtual void lock()   = 0;
  virtual void unlock() = 0;
  virtual ~SyncStrategy() = default;
};

struct CoarseGrained : SyncStrategy {
  std::mutex m_;
  void lock()   override { m_.lock(); }
  void unlock() override { m_.unlock(); }
};

struct Atomic : SyncStrategy {
  std::atomic_flag f_ = ATOMIC_FLAG_INIT;
  void lock()   override {
    while (f_.test_and_set(std::memory_order_acquire));
  }
  void unlock() override { f_.clear(std::memory_order_release); }
};

class SimEngine {
  std::unique_ptr<SyncStrategy> sync_;
  int steps_ = 0;
public:
  explicit SimEngine(std::unique_ptr<SyncStrategy> s)
    : sync_(std::move(s)) {}

  void run_step() {
    sync_->lock();
    ++steps_;
    sync_->unlock();
  }

  int steps() const { return steps_; }
};

int main() {
  SimEngine coarse_engine{std::make_unique<CoarseGrained>()};
  SimEngine atomic_engine{std::make_unique<Atomic>()};

  for (int i = 0; i < 1000; ++i) coarse_engine.run_step();
  for (int i = 0; i < 1000; ++i) atomic_engine.run_step();

  std::cout << coarse_engine.steps() << " " << atomic_engine.steps();
}

Participants

ContextStrategyConcreteStrategyAConcreteStrategyB

Where it is used

C++ standard library

std::sort(begin, end, comparator) — sort algorithm fixed, ordering strategy injected as a callable.

Multicore simulators

Celeris plugs in CoarseGrained, FineGrained, or Atomic sync strategy without touching the engine loop.

Payment gateways

A checkout service swaps Stripe/PayPal/Razorpay by replacing one strategy object — no checkout logic changes.

Compression codecs

A file archiver selects zstd, lz4, or brotli at runtime — all implement compress()/decompress().

ObserverCommand