Skip to Content
Course content

162: The Strategy Pattern

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've probably encountered a situation where you have a function that does three or four different things depending on a flag or a type passed into it. Usually, this starts as a simple if/else block. But as the project grows, that block turns into a monster. That's where the Strategy Pattern comes in. It's essentially a way to swap out the "how" of an operation without changing the "what".

The "Switch" Trap

Let's say we're building a shipping calculator for an e-commerce site. We support a few different carriers, and each has its own pricing logic. I'll start the way I usually do when I'm rushing—by just jamming everything into one function.

const shippingCalculator = (carrier, weight) => {
  if (carrier === 'fedex') {
    return weight * 1.5 + 10;
  } else if (carrier === 'ups') {
    return weight * 1.2 + 5;
  } else if (carrier === 'usps') {
    return weight * 0.8 + 2;
  }
  throw new Error('Unsupported carrier');
};

This works fine for three carriers. But here is where I usually make the mistake: I assume it'll stay this way. A week later, the business wants to add DHL, then overnight shipping, then international rates. Suddenly, this one function is 200 lines long, and every time I touch it to add a new carrier, I risk breaking the pricing for the existing ones. This violates the "Open/Closed Principle"—our code should be open for extension, but closed for modification.

Defining Our Strategies

Instead of one giant function, I want to treat each shipping method as a "strategy." In JavaScript, we don't need complex class hierarchies for this; simple objects or functions will do. I'll define each algorithm independently.

const fedexStrategy = {
  calculate: (weight) => weight * 1.5 + 10
};

const upsStrategy = {
  calculate: (weight) => weight * 1.2 + 5
};

const uspsStrategy = {
  calculate: (weight) => weight * 0.8 + 2
};

Now, these are isolated. If the FedEx pricing changes, I only touch the fedexStrategy. I don't even have to look at the UPS or USPS code, which means I can't accidentally break them.

Wiring it together with a Context

Now we need a way to use these strategies without the calling code needing to know the details of each calculation. We create a "Context" object. I like to think of the Context as the manager: it doesn't know how to calculate the shipping itself, it just knows who to ask.

class ShippingContext {
  constructor(strategy) {
    this.strategy = strategy;
  }

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

  calculatePrice(weight) {
    return this.strategy.calculate(weight);
  }
}

// Let's use it
const weight = 10;
const context = new ShippingContext(fedexStrategy);
console.log(`FedEx: ${context.calculatePrice(weight)}`); // 25

context.setStrategy(upsStrategy);
console.log(`UPS: ${context.calculatePrice(weight)}`); // 17

Notice how the ShippingContext doesn't contain a single if statement. It just calls .calculate(). It doesn't care which strategy it's using, as long as that strategy has a calculate method. This is the core of the pattern: we've decoupled the execution logic from the specific implementation.

If you need to add a new carrier now, you just create a new strategy object and pass it in. You never have to touch the ShippingContext class ever again. That's a huge win for stability in a production codebase.




📋 Practical Task

Build a Dynamic Discount Engine

You are building a checkout system that applies different discount strategies based on the customer's membership level (e.g., Regular, Silver, Gold). Instead of using a switch statement in your checkout function, implement the Strategy Pattern.

  • Create three strategy objects: regularDiscount (0% off), silverDiscount (10% off), and goldDiscount (20% off). Each should have a calculate method that takes a total price and returns the discounted price.
  • Create a DiscountContext class that accepts a strategy in its constructor and has a applyDiscount(amount) method.
  • Demonstrate the code by initializing the context with silverDiscount, calculating the price for $100, and then switching the strategy to goldDiscount to calculate the price for the same amount.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.