← Back to PatternPip

STRATEGY / QUICK REFERENCE

The pattern,
at a glance.

Swap an algorithm behind a shared contract. Keep the context that uses it stable.

What varies?
The algorithm used to do a job.
What stays stable?
The context that requests the work, and the contract it relies on.
Where is the choice made?
In the caller or configuration, outside the context.

The four roles

Strategy SortStrategy
The shared operation and behavioral promises every implementation must fulfill.
Concrete strategy BubbleSort / QuickSort
A particular way to perform the job. Each can be used through the same contract.
Context Sorter
Holds a strategy and delegates the work. It does not select algorithms by name.
Caller / configuration
Chooses an implementation and supplies it to the context. The choice still exists here.

The shape in TypeScript

Excerpts from the sorting lesson. SortRun contains the original input, sorted output, and operation trace. Every strategy preserves the input and all duplicates; stable ordering of equal items is not required.

1. Keep the contract and context stable.

export interface SortStrategy {
  sort(input: readonly number[]): SortRun;
}

export class Sorter {
  private strategy: SortStrategy;
  constructor(strategy: SortStrategy) {
    this.strategy = strategy;
  }
  setStrategy(strategy: SortStrategy) {
    this.strategy = strategy;
  }
  sort(input: readonly number[]): SortRun {
    return this.strategy.sort(input);
  }
}

2. Choose outside the context.

export function sortWithStrategy(
  algorithm: Algorithm,
  input: readonly number[],
): SortRun {
  const strategy = algorithm === "quick" ? new QuickSort() : new BubbleSort();
  return new Sorter(strategy).sort(input);
}

This caller creates a context for each request. To replace behavior on an existing context, call setStrategy with another conforming strategy, then call sort again.

See the concrete strategies
export class BubbleSort implements SortStrategy {
  sort(input: readonly number[]): SortRun {
    return bubbleSort(input); // Existing tested algorithm, including its trace.
  }
}

export class QuickSort implements SortStrategy {
  sort(input: readonly number[]): SortRun {
    return quickSort(input); // Existing tested algorithm, including its trace.
  }
}

These excerpts use the lesson’s sorting functions and shared types. The walkthrough runs these TypeScript implementations.

Use it for changing behavior.

A good fit

Several interchangeable ways to do one job; algorithms that vary independently; behavior chosen by configuration or replaced at runtime.

A simpler option may be enough

One stable implementation or a small conditional. A function parameter can express Strategy too—classes are not mandatory.

Common mistakes

  • Moving the algorithm-name switch into the context. Keep selection in the caller so adding a strategy does not require editing the context.
  • Matching the method signature but breaking its promises. A replacement must also preserve agreed behavior, such as keeping the input unchanged. Types alone do not enforce every guarantee.
  • Expecting every edit—or every performance problem—to disappear. A new strategy still needs implementation, tests and registration. Strategy changes the organization of code, not an algorithm’s speed.