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. The client selects the appropriate strategy at runtime without modifying the context.

Intent and Motivation

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

Motivation: A navigation app supports walking, driving, and transit routes — each uses a different algorithm. Hard-coding if (mode == DRIVING) inside Navigator violates Open/Closed Principle and grows unwieldy. Strategy extracts each algorithm into its own class (DrivingStrategy, WalkingStrategy) implementing a RouteStrategy interface. The Navigator context holds a reference to the current strategy and delegates buildRoute() to it. Switching strategies at runtime is a single setStrategy() call.

Strategy is one of the most practical patterns for eliminating conditional complexity and enabling plugin-like extensibility.

Structure (UML-like)

  ┌──────────────┐  uses         ┌──────────────┐
│   Context    │ ────────────► │   Strategy   │ (interface)
├──────────────┤               ├──────────────┤
│ - strategy   │               │ + execute()  │
│ + setStrategy│               └──────▲───────┘
│ + doSomething│                      │ implements
└──────────────┘               ┌───────┴──────────────┐
                               │                      │
                        ┌──────┴──────┐       ┌───────┴──────┐
                        │ ConcreteSt. │       │ ConcreteSt.  │
                        │     A       │       │      B       │
                        └─────────────┘       └──────────────┘
  

Participants:

  • Strategy — common interface for all supported algorithms.
  • ConcreteStrategy — implements a specific algorithm.
  • Context — maintains a reference to a Strategy; delegates algorithm execution to it.
  • Client — creates and configures the Context with a ConcreteStrategy.

Java Example

  import java.util.*;

// Strategy
interface SortStrategy {
    void sort(List<Integer> list);
}

class BubbleSort implements SortStrategy {
    public void sort(List<Integer> list) {
        System.out.println("Sorting with bubble sort");
        Collections.sort(list); // simplified
    }
}

class QuickSort implements SortStrategy {
    public void sort(List<Integer> list) {
        System.out.println("Sorting with quick sort");
        list.sort(Integer::compareTo);
    }
}

// Context
class Sorter {
    private SortStrategy strategy;

    Sorter(SortStrategy strategy) { this.strategy = strategy; }

    void setStrategy(SortStrategy strategy) { this.strategy = strategy; }

    void sort(List<Integer> list) { strategy.sort(list); }
}

// Usage
List<Integer> data = new ArrayList<>(List.of(5, 3, 8, 1));
Sorter sorter = new Sorter(new BubbleSort());
sorter.sort(data);

sorter.setStrategy(new QuickSort());
sorter.sort(data);
  

JavaScript Example

  const paymentStrategies = {
  creditCard: {
    pay(amount) {
      console.log(`Paid $${amount} via Credit Card`);
    }
  },
  paypal: {
    pay(amount) {
      console.log(`Paid $${amount} via PayPal`);
    }
  },
  crypto: {
    pay(amount) {
      console.log(`Paid $${amount} via Crypto`);
    }
  }
};

class Checkout {
  constructor(strategy) {
    this.strategy = strategy;
  }

  setPaymentMethod(method) {
    this.strategy = paymentStrategies[method];
    if (!this.strategy) throw new Error(`Unknown method: ${method}`);
  }

  processPayment(amount) {
    this.strategy.pay(amount);
  }
}

const checkout = new Checkout(paymentStrategies.creditCard);
checkout.processPayment(99.99);
checkout.setPaymentMethod('paypal');
checkout.processPayment(49.99);
  

Real-World Use Cases

Framework / System Usage
Java Comparator Sorting strategy injected into Collections.sort().
Java ThreadPoolExecutor Rejection policies (Abort, CallerRuns, Discard) are strategies.
Spring @Qualifier Selects among multiple strategy bean implementations.
Express middleware Authentication strategies (JWT, OAuth, API key) swapped per route.
Compression libraries gzip, bzip2, lz4 algorithms as interchangeable strategies.
Machine learning Optimizer strategies (SGD, Adam, RMSprop) plugged into training loops.

Pros and Cons

Pros Cons
Algorithms can be swapped at runtime Clients must be aware of available strategies
Eliminates conditional statements for algorithm selection Increased number of objects (one per strategy)
Open/Closed — add strategies without modifying context Overkill when only one algorithm will ever exist
Each strategy is independently testable Context must expose setStrategy() — clients control selection
Strategies can be shared across multiple contexts Strategy selection logic can itself become complex

When to Use vs When NOT to Use

Use when:

  • Many related classes differ only in their behavior (algorithm).
  • You need different variants of an algorithm.
  • An algorithm uses data that clients should not know about.
  • A class defines many behaviors appearing as conditional statements.

Do NOT use when:

  • Only one algorithm exists and is unlikely to change.
  • Algorithm selection is simple and stable (a single function or method suffices).
  • Strategies differ only in configuration, not algorithm (use parameters instead).
  • The overhead of strategy objects outweighs the flexibility benefit.

Common Mistakes

  1. Confusing Strategy with State — Strategy is chosen externally; State transitions internally based on context conditions.
  2. Context knowing concrete strategy classes — depend on the Strategy interface only.
  3. Strategy with state — strategies should be stateless when possible; stateful strategies complicate sharing.
  4. Too many strategies for minor variations — use parameters or configuration for small differences.
  5. Not using dependency injection — manually new-ing strategies in context couples to concrete classes.
  • State — similar structure; State changes autonomously, Strategy is client-selected.
  • Command — commands encapsulate actions; strategies encapsulate algorithms.
  • Template Method — defines algorithm skeleton in a base class; Strategy delegates entire algorithm to a separate object.
  • Bridge — Strategy varies behavior; Bridge varies implementation platform.
  • Factory Method — can create the appropriate strategy based on configuration.