Java 4 min read

The Strategy Design Pattern: Kill the If-Else Statements

Master the Strategy Pattern in Java. Learn how to replace complex if-else logic with interchangeable algorithms and follow the Open/Closed Principle.

MR

Moshiour Rahman

Advertisement

The Problem: The If-Else Monster

Imagine you are building an E-Commerce checkout implementation. You need to handle payments.

public void pay(String type, int amount) {
    if (type.equals("CREDIT_CARD")) {
        // ... 20 lines of credit card logic ...
        System.out.println("Paid with Card");
    } else if (type.equals("PAYPAL")) {
        // ... 20 lines of PayPal logic ...
        System.out.println("Paid with PayPal");
    } else if (type.equals("CRYPTO")) {
        // ... 20 lines of Crypto logic ...
        System.out.println("Paid with Crypto");
    } else {
        throw new IllegalArgumentException("Unknown method");
    }
}

Why this is bad:

  1. Violation of Open/Closed Principle: Every time you add a new payment method (e.g., Apple Pay), you must modify the pay class. This risks breaking existing code.
  2. Hard to Read: The method grows indefinitely.
  3. Hard to Test: You have to test all logic paths in one giant method.

The Solution: The Strategy Pattern

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it.

Real-Life Analogy: GPS Navigation 🗺️

When you use Google Maps, you select a destination. Then you choose a Strategy:

  • 🚗 Driving: Optimized for roads, avoiding traffic.
  • 🚶 Walking: Optimized for sidewalks and parks.
  • 🚌 Transit: Optimized for bus/train schedules.

The Goal (A to B) is the same. The Strategy (How to get there) creates different paths.

Visualizing the Pattern

Strategy Pattern

Implementation

1. The Interface

Define the common contract for all strategies.

public interface PaymentStrategy {
    void pay(int amount);
}

2. Concrete Strategies

Implement the specific logic in separate classes.

public class CreditCardStrategy implements PaymentStrategy {
    private String cardNumber;

    public CreditCardStrategy(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with Credit Card: " + cardNumber);
    }
}

public class PayPalStrategy implements PaymentStrategy {
    private String email;

    public PayPalStrategy(String email) {
        this.email = email;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using PayPal: " + email);
    }
}

3. The Context

The class that uses the strategy. It doesn’t know which strategy it’s using, just that it fulfills the interface.

public class ShoppingCart {
    // Rely on abstraction, not implementation
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout(int amount) {
        if (paymentStrategy == null) {
            throw new IllegalStateException("Payment method not selected");
        }
        paymentStrategy.pay(amount);
    }
}

Usage

ShoppingCart cart = new ShoppingCart();

// User selects PayPal
cart.setPaymentStrategy(new PayPalStrategy("user@example.com"));
cart.checkout(100);

// User switches to Credit Card
cart.setPaymentStrategy(new CreditCardStrategy("1234-5678-9012-3456"));
cart.checkout(200);

In The Wild (Real World Examples)

1. java.util.Collections.sort()

When you sort a list, you provide a Comparator. The sorting algorithm (MergeSort/TimSort) is fixed, but the Comparison Strategy is customized by you.

// We pass a "Strategy" for comparing integers
Collections.sort(numbers, (a, b) -> b - a); 

2. Spring Framework (Resource)

Spring uses different strategies to load resources depending on the prefix (classpath:, file:, http:).

Cheat Sheet

FeatureDetails
CategoryBehavioral
Problem SolvedMassive switch/if-else statements, rigid algorithms
Key PrincipleOpen/Closed Principle
Signature LookA class accepting an Interface in its setters or constructor (setAlgorithm(Algorithm a))
ProsSwappable logic at runtime, cleaner code, easier testing
ConsIncreases number of classes

Tips to Remember 🧠

  • “The Plug Adapter”: Think of it like plugging different distinct cartridges into a game console.
  • Runtime Switching: If you need to change behavior while the app is running (e.g., Light Mode vs Dark Mode), use Strategy.
  • Composition over Inheritance: Instead of class Dog extends Animal, use class Animal { MoveStrategy ms; }. This lets a Dog switch from “Walking” to “Swimming” without changing its class.

Advertisement

MR

Moshiour Rahman

Software Architect & AI Engineer

Share:
MR

Moshiour Rahman

Software Architect & AI Engineer

Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.

Related Articles

Comments

Comments are powered by GitHub Discussions.

Configure Giscus at giscus.app to enable comments.