Sitemap

When If–Else Starts to Scream, It’s Time for a Strategy

3 min readNov 17, 2025

--

Once upon a time, in a quite little codebase, there lived a function called calculate() which had one simple job: calculate prices.

But as time passed, business got more complicated. Different customers needed different rules.

  • VIP customers needed a discount
  • Users in Germany needed a tax added
  • Italian customers had their own pricing logic
  • And holiday sales changed everything again

So, someone added a few if statements, then a few More and More…Until a calculate() turned into a monster of conditions.

double calculate(Order order) {
double price = order.getTotal();
if (order.isVip()) {
price = price * 0.9;
} else if (order.getCountry().equals("DE")) {
price = price * 1.19;
} else if (order.getCountry().equals("IT")) {
price = price + order.getServiceFee();
} else if (isHoliday()) {
price = price * 0.8;
}
return price;
}

Still, nothing technically wrong with this code. It is readable, easy to follow, and it works.

But business rules always change. Sooner or later, more pricing variations will appear. And every new condition means opening this method again, editing it, testing it, and just hoping nothing breaks.

That’s a code smell and a sign of tight coupling. Our business logic and calculation logic are mixed together here.

Before this gets too messy to manage, let’s pause and rethink our approach.

At this point, our poor calculate() function had become a bit of a drama queen. Every new business rule made it scream louder: “I can’t handle this!”

Maybe it’s time to let calculate() stop trying to do everything by itself. And that’s when…enter the Strategy Pattern!

What’s a Strategy Pattern?

Think of it like this- instead of one big function that tries to handle every single situation, we create different “strategies” which will be small, focused classes where each class is handling its own pricing rule.

Each strategy knows exactly how to calculate the price for a certain situation. No more “if VIP do this, if German do that…” mess.

Here’s the idea:

Instead of one big function handling all the cases, we define a PricingStrategy interface and create small, focused classes for each pricing rule.

Get Sreelekha Mulinti’s stories in your inbox

Join Medium for free to get updates from this writer.

First, we define a simple interface that all pricing strategies will follow:

interface PricingStrategy {
double calculatePrice(Order order);
}

Now, each special rule gets its own class which is clear, focused, and easy to extend:

class VipPricingStrategy implements PricingStrategy {
public double calculatePrice(Order order) {
return order.getTotal() * 0.9;
}
}
class GermanPricingStrategy implements PricingStrategy {
public double calculatePrice(Order order) {
return order.getTotal() * 1.19;
}
}
class ItalianPricingStrategy implements PricingStrategy {
public double calculatePrice(Order order) {
return order.getTotal() + order.getServiceFee();
}
}
class HolidayPricingStrategy implements PricingStrategy {
public double calculatePrice(Order order) {
return order.getTotal() * 0.8;
}
}

Choosing the Right Strategy

Now we need something to decide which strategy to use. Instead of adding more if statements inside our calculate() function, we move that logic into a factory

class PricingStrategyFactory {
public static PricingStrategy getStrategy(Order order) {
if (order.isVip()) return new VipPricingStrategy();
if ("DE".equals(order.getCountry())) return new GermanPricingStrategy();
if ("IT".equals(order.getCountry())) return new ItalianPricingStrategy();
if (isHoliday()) return new HolidayPricingStrategy();
return new DefaultPricingStrategy();
}
}

Now, if/else lives only in the factory, whose only responsibility is selecting which strategy to use but not how to calculate it.

The Final Step: Let the Calculator Do Its Job

Our PriceCalculatornow doesn’t care about business rules at all. It simply delegates to the right strategy:

class PriceCalculator {
private final PricingStrategy strategy;
    public PriceCalculator(PricingStrategy strategy) {
this.strategy = strategy;
}
public double calculate(Order order) {
return strategy.calculatePrice(order);
}
}

And the usage is clean and readable:

Order order = new Order(...);
PricingStrategy strategy = PricingStrategyFactory.getStrategy(order);
PriceCalculator calculator = new PriceCalculator(strategy);
double finalPrice = calculator.calculate(order);

Why This Is Awesome

  • Easy to extend: New rule? Just add a new strategy class.
  • Safe: No need to touch existing code, reducing the risk of breaking something else.
  • Clean and readable: Each rule lives in its own class.
  • Follows SOLID principles: Especially the Open/Closed Principle -> open for extension, closed for modification.

Wrap Up

Our little calculate() function can finally rest.😴

It’s no longer trying to juggle all the rules itself. Each rule has its own home, and everything feels calm and organized again. And that’s the beauty of the Strategy Pattern it turns chaos into clarity.

So next time your code starts screaming under a pile of if statements, just remember:

“Maybe it is time to call in a Strategy”

--

--