lesson 1/1 · 0:59
Strategy Pattern
Add a new payment method without touching Checkout, by turning each branch of a switch into its own class.
“Define a family of algorithms, encapsulate each one, and make them interchangeable.”
The problem in code
Your checkout has one switch with a branch for every payment method: card, PayPal, Apple Pay. Adding Klarna means opening it again and retesting every branch.
class Checkout {
pay(method: 'card' | 'paypal' | 'applepay', amount: number) {
switch (method) {
case 'card': /* charge card */ break;
case 'paypal': /* redirect to PayPal */ break;
case 'applepay': /* Apple Pay sheet */ break;
// Klarna: open this file again, retest every branch
}
}
}
The fix
Each payment method becomes its own class with one pay method. Checkout holds a strategy and calls pay, without asking which one it is.
interface PaymentStrategy {
pay(amount: number): Promise<void>;
}
class CardPayment implements PaymentStrategy {
async pay(amount: number) { /* charge card */ }
}
class PayPalPayment implements PaymentStrategy {
async pay(amount: number) { /* redirect to PayPal */ }
}
class Checkout {
constructor(private strategy: PaymentStrategy) {}
complete(amount: number) {
return this.strategy.pay(amount);
}
}
// The customer picks at runtime.
const checkout = new Checkout(choice === 'paypal' ? new PayPalPayment() : new CardPayment());
// Klarna is one new class. Checkout and its tests stay untouched.
class KlarnaPayment implements PaymentStrategy {
async pay(amount: number) { /* Klarna flow */ }
}
When not to use it
Only two branches that never change? Keep the if. A pattern is worth it when the list of options keeps growing or each option has real logic of its own.
Takeaway
Strategy swaps how something is done without changing the code that asks for it. It’s the Open/Closed Principle from the SOLID track, applied to one behavior.