← Back to TypeScript Course | Chapter 16: Advanced Patterns | Lesson 5 of 7

Strategy Pattern

The Strategy Pattern places interchangeable algorithms behind a common interface. A context can switch strategies without changing its core logic.

Basic Strategy

A strategy represents one algorithm behind a common interface, while a context object holds a reference to whichever strategy it was given and delegates the actual work to it. This means the context never needs an if/else chain to pick behavior — it just calls the strategy it was handed.

Example: Basic Strategy

typescript
interface PaymentStrategy {
  pay(amount: number): string;
}
class CreditCard implements PaymentStrategy {
  pay(amount: number) { return `Paid ${amount} by credit card`; }
}
class Context {
  constructor(private strategy: PaymentStrategy) {}
  checkout(amount: number) { return this.strategy.pay(amount); }
}
console.log(new Context(new CreditCard()).checkout(100));

Switching Strategies

A context can expose a setter so its strategy can be swapped while the program is running, letting the same object change behavior in response to user input, configuration, or runtime conditions without being recreated.

Example: Switching Strategies

typescript
interface PaymentStrategy {
  pay(amount: number): string;
}
class PayPal implements PaymentStrategy {
  pay(amount: number) { return `Paid ${amount} via PayPal`; }
}
class Context {
  private strategy: PaymentStrategy;
  constructor(strategy: PaymentStrategy) { this.strategy = strategy; }
  setStrategy(strategy: PaymentStrategy) { this.strategy = strategy; }
  checkout(amount: number) { return this.strategy.pay(amount); }
}
const ctx = new Context(new PayPal());
console.log(ctx.checkout(50));

Function Strategies

For simple algorithms, a typed function can serve as a strategy without creating a separate class for it — TypeScript's structural typing means any function matching the expected signature is automatically compatible.

Example: Function Strategies

typescript
type SortStrategy = (a: number, b: number) => number;
const ascending: SortStrategy = (a, b) => a - b;
const descending: SortStrategy = (a, b) => b - a;
function sortWith(nums: number[], strategy: SortStrategy) {
  return [...nums].sort(strategy);
}
console.log(sortWith([3, 1, 2], ascending));

Benefits of Strategy

Strategy reduces large conditional blocks and isolates each algorithm so it can be tested, replaced, or extended independently, which matters most once a codebase has several interchangeable variants of the same operation.

Example: Benefits of Strategy

typescript
interface Discount {
  apply(price: number): number;
}
class NoDiscount implements Discount {
  apply(price: number) { return price; }
}
class TenPercentOff implements Discount {
  apply(price: number) { return price * 0.9; }
}
function checkout(price: number, discount: Discount) {
  return discount.apply(price);
}
console.log(checkout(100, new TenPercentOff()));

When to Use Strategy

Use Strategy when an operation genuinely has multiple interchangeable approaches that might change at runtime or grow over time. Avoid it when one simple algorithm is sufficient — the extra interface adds indirection you don't need yet.

Example: When to Use Strategy

typescript
interface Formatter {
  format(value: number): string;
}
class PlainFormatter implements Formatter {
  format(value: number) { return String(value); }
}
function display(value: number, formatter: Formatter) {
  return formatter.format(value);
}
console.log(display(42, new PlainFormatter()));
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.