Strategy Pattern
Strategy Pattern
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Intent
The Strategy pattern extracts each variant algorithm into its own class behind a common interface. The Context holds a reference to a Strategy and delegates execution to it. The client (or the Context itself) calls setStrategy() to swap the algorithm at runtime. No concrete strategy class ever changes the Context's strategy reference — that swap is always external. This is the defining difference from the State-Pattern where the State object drives its own transitions.
When NOT to Use
- Only one algorithm exists and will never vary — Strategy is premature abstraction with no payoff
- The client never needs to change strategies at runtime — a direct method call is simpler
- The number of potential strategies is small and fixed — use a simple enum switch, which is more readable
- Strategies need access to private Context state — this breaks encapsulation and signals the algorithm should live inside the Context
When to Use
- Multiple interchangeable algorithms exist for the same behaviour (sort, compress, validate, price)
- Algorithm choice depends on runtime conditions (user config, environment, input characteristics)
- You want to eliminate conditional logic that selects an algorithm (
if type == 'quick' ... else if type == 'merge') - Open/closed principle priority — new algorithms must be addable without modifying the Context
How It Works
Participants:
- Strategy — interface declaring the algorithm method (e.g.,
sort(data: number[]): number[]). - ConcreteStrategy — a specific algorithm implementation (
QuickSort,BubbleSort,MergeSort). Never touches the Context's strategy reference. - Context — holds a
Strategyreference, exposessetStrategy(s: Strategy)for runtime swapping, delegatesexecute()to the current strategy. The Context does not know which algorithm runs — it only knows the Strategy interface.
The client creates the Context with an initial Strategy and can call setStrategy() at any time to swap. The Context is oblivious to which algorithm is active.
Class Diagram
classDiagram
class Context {
-strategy : Strategy
+setStrategy(s : Strategy)
+executeStrategy(data)
}
class Strategy {
<<interface>>
+algorithm(data)*
}
class ConcreteStrategyA {
+algorithm(data)
}
class ConcreteStrategyB {
+algorithm(data)
}
class ConcreteStrategyC {
+algorithm(data)
}
Context o-- Strategy : swappable at runtime
Strategy <|.. ConcreteStrategyA
Strategy <|.. ConcreteStrategyB
Strategy <|.. ConcreteStrategyC
note for Context "Delegates algorithm to\nthe current Strategy object.\nSwap point: setStrategy()"
TypeScript Example
// Strategy Pattern — TypeScript
// Source: GoF Design Patterns, p.315
interface SortStrategy {
sort(data: number[]): number[];
}
class QuickSort implements SortStrategy {
sort(data: number[]) {
console.log("QuickSort applied");
return [...data].sort((a, b) => a - b); // simplified
}
}
class BubbleSort implements SortStrategy {
sort(data: number[]) {
console.log("BubbleSort applied");
return [...data].sort((a, b) => a - b); // simplified
}
}
class SortContext { // Context
constructor(private strategy: SortStrategy) {}
setStrategy(s: SortStrategy) { this.strategy = s; }
execute(data: number[]) { return this.strategy.sort(data); }
}
const ctx = new SortContext(new QuickSort());
ctx.execute([3, 1, 2]); // QuickSort applied
ctx.setStrategy(new BubbleSort());
ctx.execute([3, 1, 2]); // BubbleSort appliedJava Example
// Strategy Pattern — Java
// Source: GoF Design Patterns, p.315
interface SortStrategy {
int[] sort(int[] data);
}
class QuickSort implements SortStrategy {
public int[] sort(int[] data) {
System.out.println("QuickSort applied");
return data; // simplified
}
}
class BubbleSort implements SortStrategy {
public int[] sort(int[] data) {
System.out.println("BubbleSort applied");
return data; // simplified
}
}
class SortContext { // Context
private SortStrategy strategy;
public SortContext(SortStrategy s) { this.strategy = s; }
public void setStrategy(SortStrategy s) { this.strategy = s; }
public int[] execute(int[] data) { return strategy.sort(data); }
}Lineage Forward
Strategy → Feature Flags lineage:
The Strategy pattern is the upstream ancestor of the Feature-Flags-Pattern (created Phase 15). Feature Flags implement Strategy at deployment level: the "strategy" (feature variant A or B) is selected at runtime via a flag, enabling Canary releases and A/B testing without code deployment. The lineage: Strategy → Feature Flags → Canary-Release-Pattern (Phase 15) → Blue-Green-Deployment (Phase 15).
Related Concepts
| Pattern | Relationship |
|---|---|
| State-Pattern | Structural twin — the key difference is who controls algorithm/transition selection; see the comparison callout in the State-Pattern note |
| Template-Method-Pattern | Template Method uses inheritance for variation; Strategy uses composition — prefer Strategy when the algorithm must be swappable at runtime |
| Command-Pattern | Command encapsulates a specific request with receiver binding; Strategy encapsulates a reusable algorithm without receiver binding |
Related Frontend Patterns
- Component-Composition-Patterns — Custom hooks in React implement Strategy:
useForm()oruseAuth()are interchangeable behaviour units injected into a component without changing the component's structure. Angular'sinject()function achieves the same Strategy/Factory composition via the DI system. - State-Management-Patterns — Swapping between Flux/Redux, Signals, and Atomic store paradigms is Strategy applied to state management: each paradigm is an interchangeable algorithm for the same problem (state updates propagating to UI).
Sources
- Gamma, Helm, Johnson, Vlissides — Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994, p.315
Backlinks
Notes that link here: GoF-Patterns-MOC