CodePark/ questions / Behavioral patterns
START PRACTICINGPRACTICE
Backend question · Software design & patterns › Behavioral patterns

Refactor this switch using the Strategy pattern. What do you gain?

async function pay(method: string, amount: number) {
  switch (method) {
    case "card":   /* 30 lines of Stripe calls */ break;
    case "paypal": /* 25 lines of PayPal calls */ break;
    case "wallet": /* 20 lines of wallet logic */ break;
    default: throw new Error("Unsupported method");
  }
}

Strategy puts a family of interchangeable algorithms behind one interface. The caller picks one at runtime.

How to refactor:

  • Define interface PaymentStrategy { pay(amount: Money): Promise<PaymentResult> }.
  • Move each branch of the switch into its own class: CardPayment, PaypalPayment, WalletPayment.
  • Register them in a map such as Map<PaymentMethod, PaymentStrategy>. Build it once at startup, or let the DI container (dependency injection container) build it.
  • The checkout code becomes strategies.get(method).pay(amount), with one check for an unknown method.

What you gain:

  • To add a provider, you add a class and a registration. You don't edit a function that keeps growing. This is the Open/Closed Principle: open for extension, closed for changes.
  • Each strategy has its own dependencies, and you can unit test it alone.
  • The switch over the payment type disappears from every place that used to repeat it.

It is not always worth it. A switch with three one-line cases that rarely change is simpler as it is.

  • Strategy uses one interface and one class per algorithm.
  • The code picks the strategy at runtime from a map or the DI container.
  • A new provider means a new class, with no edits to existing code (Open/Closed Principle).
  • Each strategy is isolated and testable, but don't use the pattern for small, simple switches.