← Back to principles

Design Principle

Strategy Pattern

Learn the Strategy pattern: define algorithm families, encapsulate each one, and make them interchangeable. Algorithms vary independently from clients.

Strategy Pattern

Why This Matters

Think of the Strategy pattern like choosing a payment method at checkout. You don't change the checkout process—you just choose a different payment strategy (credit card, PayPal, bank transfer). The Strategy pattern does the same for code—it lets you choose different algorithms without changing the code that uses them.

This matters because algorithms change. You might start with a simple sorting algorithm, then need a faster one, or a stable one, or one that handles special cases. The Strategy pattern makes it easy to swap algorithms without changing the code that uses them. This follows the Open/Closed Principle—open for extension (new strategies), closed for modification (existing code).

In interviews, when someone asks "How would you refactor this code?", they're testing whether you understand the Strategy pattern. Can you identify when code has multiple algorithms that could be swapped? Can you encapsulate them as strategies? Most engineers can't. They use if/else statements and wonder why the code is hard to change.

What Engineers Usually Get Wrong

Engineers often think "Strategy pattern means always use strategies." But strategies add indirection. If you only have one algorithm and it's unlikely to change, direct implementation is fine. Use strategies when you have multiple algorithms that might change, or when you need to choose algorithms at runtime.

Engineers also confuse Strategy with Factory. Strategy is about choosing algorithms. Factory is about creating objects. They're different patterns for different purposes. Understanding the difference helps you choose the right pattern.

How This Breaks Systems in the Real World

A service was calculating discounts using if/else statements: if customer type is "premium", use premium discount; if "regular", use regular discount; etc. As more customer types were added, the if/else chain grew to 50 lines. It became hard to understand and test. The fix? Use Strategy pattern. Create a DiscountStrategy interface, with implementations for each customer type. Now adding a new customer type is just creating a new strategy, not modifying a long if/else chain.

Another story: A service was using different sorting algorithms based on data size. The logic was scattered across the codebase. When the team needed to change the algorithm selection logic, they had to modify code in 10 places. The fix? Use Strategy pattern. Centralize algorithm selection in one place. Now changing selection logic requires changes in only one place.


What is the Strategy Pattern?

Strategy Pattern provides:

  • Algorithm encapsulation: Encapsulate algorithms in separate classes
  • Interchangeability: Algorithms are interchangeable
  • Runtime selection: Select algorithm at runtime
  • Open/Closed Principle: Open for extension, closed for modification

Use cases:

  • Sorting algorithms
  • Payment processing
  • Compression algorithms
  • Validation strategies
  • Discount calculations

Structure

Context
  └─ strategy: Strategy
  └─ execute() (uses strategy.algorithm())

Strategy (interface)
  └─ algorithm()

ConcreteStrategyA (implements Strategy)
  └─ algorithm()

ConcreteStrategyB (implements Strategy)
  └─ algorithm()

Examples

Payment Strategy

1// Strategy interface
2interface PaymentStrategy {
3 pay(amount: number): void;
4}
5
6// Concrete strategies
7class CreditCardStrategy implements PaymentStrategy {
8 constructor(private cardNumber: string, private cvv: string) {}
9
10 pay(amount: number): void {
11 console.log(`Paid $${amount} using credit card ending in ${this.cardNumber

Sorting Strategy

1// Strategy interface
2interface SortStrategy {
3 sort(data: number[]): number[];
4}
5
6// Concrete strategies
7class BubbleSortStrategy implements SortStrategy {
8 sort(data: number[]): number[] {
9 console.log("Sorting using bubble sort");
10 const arr = [...data];
11 for (let i = 0; i < arr.length i

Discount Strategy

1// Strategy interface
2interface DiscountStrategy {
3 calculateDiscount(price: number): number;
4}
5
6// Concrete strategies
7class NoDiscountStrategy implements DiscountStrategy {
8 calculateDiscount(price: number): number {
9 return 0;
10 }
11}
12
13class PercentageDiscountStrategy implements DiscountStrategy {
14 constructor(private percentage: number) {}
15
16 calculateDiscount(price: number)

Common Pitfalls

  • Too many strategies: Can become complex. Fix: Group related strategies
  • Strategy selection: Hard to choose strategy. Fix: Use factory pattern
  • Context bloat: Context becomes too large. Fix: Keep context simple
  • Not using strategy: Using if-else instead. Fix: Use strategy pattern for multiple algorithms

Interview Questions

Beginner

Q: What is the Strategy pattern and when would you use it?

A:

Strategy Pattern defines a family of algorithms, encapsulates each, and makes them interchangeable.

Key characteristics:

  • Algorithm encapsulation: Each algorithm in separate class
  • Interchangeability: Algorithms are interchangeable
  • Runtime selection: Select algorithm at runtime
  • Open/Closed: Open for extension, closed for modification

Example:

1interface SortStrategy {
2 sort(data: number[]): number[];
3}
4
5class QuickSort implements SortStrategy {
6 sort(data: number[]): number[] { /* ... */ }
7}
8
9class MergeSort implements SortStrategy {
10 sort(data: number[]): number[] { /* ... */ }
11}

Use cases:

  • Multiple algorithms: Different ways to do same thing
  • Runtime selection: Choose algorithm at runtime
  • Payment methods: Credit card, PayPal, etc.
  • Sorting algorithms: Quick sort, merge sort, etc.

Benefits:

  • Flexibility: Easy to add new algorithms
  • Testability: Test each strategy independently
  • Maintainability: Changes isolated to strategy classes

Intermediate

Q: Explain how the Strategy pattern works. How does it differ from if-else or switch statements?

A:

Strategy Pattern Structure:

1. Strategy Interface:

1interface Strategy {
2 algorithm(): void;
3}

2. Concrete Strategies:

1class StrategyA implements Strategy {
2 algorithm(): void {
3 // Algorithm A
4 }
5}
6
7class StrategyB implements Strategy {
8 algorithm(): void {
9 // Algorithm B
10 }
11}

3. Context:

1class Context {
2 private strategy: Strategy;
3
4 setStrategy(strategy: Strategy): void {
5 this.strategy = strategy;
6 }
7
8 execute(): void {
9 this.strategy.algorithm();
10 }
11}

Difference from If-Else:

If-Else (Not Strategy):

1function process(type: string) {
2 if (type === "A") {
3 // Algorithm A
4 } else if (type === "B") {
5 // Algorithm B
6 }
7}

Problems:

  • Not extensible: Need to modify function for new types
  • Violates Open/Closed: Closed for extension
  • Hard to test: Can't test algorithms independently

Strategy Pattern:

1class Context {
2 private strategy: Strategy;
3
4 execute(): void {
5 this.strategy.algorithm(); // Extensible, testable
6 }
7}

Benefits:

  • Extensible: Add new strategies without modifying context
  • Testable: Test each strategy independently
  • Maintainable: Changes isolated to strategy classes

Senior

Q: Design a strategy system for a recommendation engine that uses different recommendation algorithms (collaborative filtering, content-based, hybrid). Handle algorithm selection based on user preferences, data availability, and performance requirements.

A:

1// Strategy interface
2interface RecommendationStrategy {
3 recommend(userId: string, limit: number): Promise<Recommendation[]>;
4 getAlgorithmName(): string;
5 getPerformanceMetrics(): PerformanceMetrics;
6}
7
8// Collaborative filtering strategy
9class CollaborativeFilteringStrategy implements RecommendationStrategy {
10 async recommend(userId: string, limit: number): Promise<Recommendation[]> {
11 // Find similar users

Features:

  1. Multiple strategies: Different recommendation algorithms
  2. Strategy selection: Select based on context
  3. Performance metrics: Track algorithm performance
  4. Hybrid approach: Combine multiple strategies

Failure Stories You'll Recognize

The If/Else Chain: A service was calculating discounts using if/else statements: if customer type is "premium", use premium discount; if "regular", use regular discount; etc. As more customer types were added, the if/else chain grew to 50 lines. It became hard to understand and test. The fix? Use Strategy pattern. Create a DiscountStrategy interface, with implementations for each customer type. Now adding a new customer type is just creating a new strategy, not modifying a long if/else chain.

The Scattered Algorithm Selection: A service was using different sorting algorithms based on data size. The logic was scattered across the codebase. When the team needed to change the algorithm selection logic, they had to modify code in 10 places. The fix? Use Strategy pattern. Centralize algorithm selection in one place. Now changing selection logic requires changes in only one place.

The Over-Engineered Strategy: A team used Strategy pattern for simple calculations that never changed. The abstraction added complexity without providing value. The code was harder to understand than a simple function. The fix? Use strategies only when algorithms might change or when you need to choose at runtime. For simple, stable algorithms, direct implementation is fine.

What Interviewers Are Really Testing

They want to hear you talk about Strategy as a tool for managing algorithm variations, not a rule to always follow. Junior engineers say "use Strategy pattern for algorithms." Senior engineers say "Strategy pattern encapsulates algorithms and makes them interchangeable. Use it when you have multiple algorithms that might change, or when you need to choose at runtime. Don't over-engineer—simple algorithms don't need strategies."

When they ask "How would you refactor this code?", they're testing:

  • Can you identify when code has multiple algorithms?

  • Do you know when to use Strategy vs direct implementation?

  • Can you design strategies that are simple and maintainable?

  • Strategy pattern: Encapsulates algorithms, makes them interchangeable

  • Runtime selection: Select algorithm at runtime

  • Open/Closed Principle: Open for extension, closed for modification

  • Use cases: Multiple algorithms, payment methods, sorting, validation

  • Benefits: Flexibility, testability, maintainability

  • Best practices: Use factory for selection, keep context simple, group related strategies

How InterviewCrafted Will Teach This

We'll teach this through real code problems, not abstract patterns. Instead of memorizing "Strategy pattern encapsulates algorithms," you'll learn through scenarios like "why is it hard to add a new discount type?"

You'll see how Strategy helps you manage algorithm variations and make code more flexible. When an interviewer asks "how would you refactor this code?", you'll think about when strategies help and when they don't—not just "use Strategy pattern."

  • Command Pattern - Both patterns encapsulate behavior. Strategy encapsulates algorithms, Command encapsulates operations.
  • Factory Pattern - Factories create objects, strategies choose algorithms. Understanding both helps choose the right pattern.
  • SOLID Principles - Strategy pattern follows Open/Closed Principle (open for extension, closed for modification). Understanding SOLID helps understand strategy benefits.
  • Separation of Concerns - Strategies separate algorithm selection from usage. Understanding separation of concerns helps understand strategy benefits.
  • DRY, KISS & YAGNI - Strategies can add complexity. Understanding DRY/KISS/YAGNI helps decide when strategies are worth it.

Keep exploring

Principles work best in chorus. Pair this lesson with another concept and observe how your architecture conversations change.