Strategy Pattern

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 1 min

Concept

Category: Behavioral Pattern

The Strategy Pattern defines a family of algorithms, encapsulates each one into a separate class, and makes them interchangeable. Strategy lets the algorithm vary independently from the clients that use it.

Instead of writing a massive if/else or switch statement inside your class to handle different behaviors, you extract the behaviors into their own independent objects. You can then dynamically swap these behavior objects at runtime.

The Mental Model

Imagine an E-Commerce application. At checkout, the user can pay with Credit Card, PayPal, or Bitcoin.
If you hardcode the payment logic directly into the Checkout class, you will have a massive, unreadable 1,000-line if/else block.
Worse, if you want to add Apple Pay, you have to modify the core Checkout class, risking breaking the entire checkout flow!

Instead, you create a PaymentStrategy interface. You write CreditCardStrategy, PayPalStrategy, and BitcoinStrategy. The Checkout class just holds a variable called strategy, and blindly calls strategy.pay(amount). At runtime, you inject the specific strategy the user selected.

Implementation

// 1. Define the Strategy Interface
interface PaymentStrategy {
    pay(amount: number): void;
}

// 2. Concrete Strategies (The isolated algorithms)
class CreditCardPayment implements PaymentStrategy {
    pay(amount: number) {
        console.log(`Processing $${amount} via Credit Card API...`);
    }
}

class PayPalPayment implements PaymentStrategy {
    pay(amount: number) {
        console.log(`Redirecting to PayPal for $${amount}...`);
    }
}

class BitcoinPayment implements PaymentStrategy {
    pay(amount: number) {
        console.log(`Transferring $${amount} to Bitcoin Wallet ID...`);
    }
}

// 3. The Context (The class that uses the Strategy)
class ShoppingCart {
    private strategy: PaymentStrategy;

    // We can inject a default strategy in the constructor
    constructor(strategy: PaymentStrategy) {
        this.strategy = strategy;
    }

    // We can swap the strategy dynamically at runtime!
    public setPaymentStrategy(strategy: PaymentStrategy) {
        this.strategy = strategy;
    }

    public checkout(amount: number) {
        // The Shopping Cart doesn't know HOW the payment works.
        // It just delegates the work to the currently equipped strategy!
        this.strategy.pay(amount);
    }
}

// Usage:
const cart = new ShoppingCart(new CreditCardPayment());
cart.checkout(100); // "Processing $100 via Credit Card API..."

// The user clicked the PayPal button! Swap the engine mid-flight!
cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(250); // "Redirecting to PayPal for $250..."

Why is this so powerful?

The Strategy Pattern is the ultimate manifestation of the Open/Closed Principle.
If you want to add ApplePayPayment tomorrow, you write a brand new class. You do NOT have to modify a single line of code in the ShoppingCart class, the CreditCardPayment class, or the PayPal class. The existing, tested codebase is mathematically immune to bugs caused by the new feature.

Interview Questions

Q: What is the difference between the Strategy Pattern and the State Pattern?
A: Structurally, they are virtually identical (A Context class delegating to an Interface).
The difference is intent.
In the Strategy Pattern, the Client (the user) dictates which strategy is used. The strategies are completely independent and don’t know about each other.
In the State Pattern, the object itself decides to swap its own internal state based on internal rules. (e.g., A Vending Machine swapping from WaitingForCoinState to DispensingState). The states are tightly coupled and trigger each other.

Q: How does the Strategy Pattern map to functional programming in JavaScript?
A: In JavaScript, you rarely need to define strict OOP classes and interfaces for the Strategy Pattern. Because JS supports Higher-Order Functions (passing functions as arguments), the “Strategy” is just a callback function!

// Functional Strategy Pattern
const payWithCard = (amount) => console.log(`Card: $${amount}`);
const payWithPayPal = (amount) => console.log(`PayPal: $${amount}`);

function checkout(amount, paymentStrategyCallback) {
    paymentStrategyCallback(amount);
}

checkout(100, payWithCard);

Passing a sorting function array.sort((a, b) => a - b) is literally the Strategy Pattern! You are injecting a “Sorting Strategy” into the sort algorithm at runtime.