,

Stop Writing Switch Statements: Use the Strategy Pattern Instead

Posted by

Tired of giant switch blocks? Learn how the Strategy Pattern helps you write cleaner, extensible, and testable code with real-world JavaScript examples.

Tired of giant switch blocks? Learn how the Strategy Pattern helps you write cleaner, extensible, and testable code with real-world JavaScript examples.

Introduction

We’ve all been there: you’re working on a feature, and suddenly your function grows into a 100-line switch statement. Each new case makes the function harder to read, harder to test, and a nightmare to extend.

Sure, switch works, but it locks you into rigid, unmaintainable code. Every time business rules change, you’re editing the same giant block. That’s a recipe for bugs.

This is exactly where the Strategy Pattern shines. Instead of cramming logic into a single branching statement, you define strategies (algorithms, behaviors, rules) separately and switch them dynamically at runtime.

In this article, you’ll learn:

  • ✅ Why switch Statements become a problem in real projects
  • ✅ How the Strategy Pattern solves it elegantly
  • ✅ Practical JavaScript/TypeScript examples
  • ✅ Real-world cases: payments, sorting, feature toggles
  • ✅ Gotchas to watch out for

By the end, you’ll see why the Strategy Pattern is a must-have tool for writing clean, extensible code.


The Problem With Switch Statements

Switch statements are fine for small, fixed sets of cases, but they scale poorly.

Example: Payment Processing

function processPayment(method: string, amount: number) {
switch (method) {
case "paypal":
console.log(`Paid ${amount} using PayPal`);
break;
case "stripe":
console.log(`Paid ${amount} using Stripe`);
break;
case "credit":
console.log(`Paid ${amount} using Credit Card`);
break;
default:
throw new Error("Unsupported payment method");
}
}

This works… until your PM asks you to:

  • Add Apple Pay support
  • Handle different discount rules per provider
  • Log audit trails differently

Suddenly, the switch balloons into an untestable monster. Every change means touching the same brittle block of code.


Enter the Strategy Pattern

The Strategy Pattern is a behavioral design pattern that lets you define a family of algorithms, encapsulate each one, and make them interchangeable at runtime.

👉 Instead of switching on behavior, you delegate it to the right strategy.

Analogy

Think of a payment counter at a mall. The cashier doesn’t care whether you use PayPal, a credit card, or cash. They just call the right payment strategy and move on.


Implementing Strategy Pattern in JavaScript/TypeScript

Step 1: Define a Common Interface

interface PaymentStrategy {
pay(amount: number): void;
}

Step 2: Create Concrete Strategies

class PayPalPayment implements PaymentStrategy {
pay(amount: number) {
console.log(`Paid ${amount} using PayPal`);
}
}

class StripePayment implements PaymentStrategy {
pay(amount: number) {
console.log(`Paid ${amount} using Stripe`);
}
}

class CreditCardPayment implements PaymentStrategy {
pay(amount: number) {
console.log(`Paid ${amount} using Credit Card`);
}
}

Step 3: Context That Uses a Strategy

class PaymentContext {
constructor(private strategy: PaymentStrategy) {}

setStrategy(strategy: PaymentStrategy) {
this.strategy = strategy;
}

pay(amount: number) {
this.strategy.pay(amount);
}
}

Step 4: Use It in Real Code

const payment = new PaymentContext(new PayPalPayment());
payment.pay(100); // Paid 100 using PayPal

payment.setStrategy(new StripePayment());
payment.pay(250); // Paid 250 using Stripe

Now, adding Apple Pay means just creating a new strategy; no need to touch existing code.


Real-World Scenarios

1. Sorting Algorithms

Switch between quicksort, mergesort, or bubble sort at runtime based on dataset size.

2. Feature Toggles

Define different rollout strategies (percentage rollout, region-based, A/B testing).

3. Authentication

Swap between OAuth, JWT, or session-based authentication dynamically.

4. Game Development

Character movement strategies (walk, fly, swim) without giant conditionals.


Benefits of the Strategy Pattern

  • No more bloated switch/if-else blocks
  • Open/Closed Principle friendly: add new strategies without editing old code
  • Unit testing made simple: test each strategy in isolation
  • Runtime flexibility: change strategy dynamically

Gotchas to Watch Out For

  • Overengineering risk: If you only have two small cases, a switch is fine. Don’t apply patterns blindly.
  • Too many small classes: Organize them logically (e.g., inside a /strategies folder).
  • Context switching costs: If strategies share state, be mindful of dependencies.

Pro Tip: Strategy with Maps

For simpler cases, you can implement a lightweight Strategy Pattern using a JavaScript object instead of classes:

const strategies: Record<string, (amount: number) => void> = {
paypal: (amt) => console.log(`Paid ${amt} using PayPal`),
stripe: (amt) => console.log(`Paid ${amt} using Stripe`),
credit: (amt) => console.log(`Paid ${amt} using Credit Card`),
};

function processPayment(method: string, amount: number) {
const strategy = strategies[method];
if (!strategy) throw new Error("Unsupported payment method");
strategy(amount);
}

processPayment("paypal", 100);
processPayment("stripe", 200);

This is Strategy in spirit, with less boilerplate.


Conclusion

The next time you’re staring at a growing switch statement, ask yourself:

👉 “Am I defining different strategies here?”

If yes, pull that logic into a Strategy Pattern. You’ll end up with code that’s easier to maintain, test, and extend, especially in growing projects.


Call to Action

Have you refactored a messy switch statement into a cleaner pattern before?
Drop your story in the comments 👇

And if your teammate keeps writing 300-line switch blocks, share this post with them. You’ll both thank me later.

Leave a Reply

Your email address will not be published. Required fields are marked *