Strategy Pattern in Java – Swap Algorithms at Runtime with Clean Design

Illustration for Strategy Pattern in Java – Swap Algorithms at Runtime with Clean Design
By Last updated:

Introduction

The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm’s behavior at runtime. Instead of hardcoding logic, this pattern encapsulates algorithms into separate classes and makes them interchangeable.

It promotes the Open/Closed Principle, allowing code to be open for extension but closed for modification. Whether you're building sorting engines, payment gateways, or compression tools—Strategy Pattern provides clean, extensible solutions.


☑️ Core Intent and Participants

Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Participants:

  • Strategy (Interface): Declares a method the context uses.
  • Concrete Strategies: Implement different variations of the algorithm.
  • Context: Maintains a reference to a Strategy and delegates work to it.

UML Diagram (Textual)

+-------------------+
|     Context       |
+-------------------+
| - strategy: Strategy |
| +setStrategy()    |
| +execute()        | ---> strategy.execute()
+-------------------+
         |
         v
+-------------------+        +-------------------+
| ConcreteStrategyA |        | ConcreteStrategyB |
+-------------------+        +-------------------+
| +execute()        |        | +execute()        |
+-------------------+        +-------------------+

🚀 Real-World Use Cases

  • Payment systems: CreditCard, PayPal, UPI, Wallet
  • Sorting strategies: QuickSort, MergeSort, BubbleSort
  • Compression tools: ZIP, GZIP, RAR
  • Game AI: Easy, Medium, Hard difficulty levels
  • File exporting: Export as PDF, DOCX, CSV

💡 Java Implementation

Step 1: Define the Strategy Interface

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

Step 2: Implement Concrete Strategies

public class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card.");
    }
}

public class PayPalPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using PayPal.");
    }
}

Step 3: Context Class

public class ShoppingCart {
    private PaymentStrategy paymentStrategy;

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

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

Step 4: Demo

public class Demo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        cart.setPaymentStrategy(new CreditCardPayment());
        cart.checkout(100); // Paid 100 using Credit Card.

        cart.setPaymentStrategy(new PayPalPayment());
        cart.checkout(250); // Paid 250 using PayPal.
    }
}

✅ Pros and Cons

✅ Pros

  • Easily swap strategies at runtime
  • Promotes clean separation of concerns
  • Adheres to Open/Closed Principle
  • Encourages interface-driven design

❌ Cons

  • Increases number of classes
  • Clients must be aware of different strategies
  • Overhead for simple scenarios

🔥 Anti-patterns and Misuse

  • Using if-else or switch instead of proper polymorphism
  • Leaking strategy logic into the context class
  • Overcomplicating with too many trivial strategies

Pattern Purpose
Strategy Swap interchangeable algorithms dynamically
State Change behavior based on internal state
Template Define skeleton with variable steps
Command Encapsulate actions as objects

🟡 Strategy vs State: Strategy is externally set; State is internal and transitions dynamically.


🔄 Refactoring Legacy Code

Refactor switch/if-else blocks that select algorithm behavior into strategy classes. This removes tight coupling and boosts extensibility.


🧠 Best Practices

  • Use interface or functional interface for strategies
  • Hide strategy selection logic from client when possible
  • Use Dependency Injection for configuring strategies
  • Prefer enums with behavior or lambdas for simple strategies

🧩 Real-World Analogy

Navigation App (e.g., Google Maps)
You can switch between driving, walking, cycling, or public transport modes. The underlying route algorithm changes depending on your selected mode—just like swapping strategies.


🧪 Java Version Relevance

✅ With Java 8+

Use lambdas for inline strategies:

PaymentStrategy upiStrategy = amount -> System.out.println("Paid " + amount + " using UPI.");
cart.setPaymentStrategy(upiStrategy);
cart.checkout(500);

✅ With Java 17+

Use sealed interfaces for stricter modeling of limited strategy options.

public sealed interface PaymentStrategy permits CreditCardPayment, PayPalPayment { ... }

📝 Conclusion and Key Takeaways

  • Strategy Pattern is ideal when you want multiple interchangeable algorithms.
  • It simplifies adding new behaviors without modifying existing code.
  • Perfect for systems that demand flexibility in logic.

✅ Use when: You have multiple interchangeable behaviors.
🚫 Avoid when: You have just one behavior or the logic is trivial.


❓ FAQ – Strategy Pattern in Java

1. What is the Strategy Pattern used for?

To encapsulate and switch between multiple algorithms or behaviors at runtime.

2. How is Strategy Pattern different from State Pattern?

Strategy is set externally by clients. State changes dynamically based on context.

3. Can I use lambdas as strategies in Java?

Yes. Lambdas are ideal for simple, one-method strategies in Java 8+.

4. What’s the benefit of Strategy Pattern?

It makes systems more flexible and easier to extend with new logic.

5. Is Strategy Pattern thread-safe?

Depends on implementation. Stateless strategies are typically safe.

6. Should strategies be injected or created manually?

Use dependency injection (e.g., Spring) for better testability and management.

7. Is Strategy Pattern overkill for simple behaviors?

Yes. Avoid it when behavior is unlikely to change or only one variation exists.

8. How does Strategy help in testing?

Each strategy can be unit tested independently without touching the context.

9. What’s the downside of Strategy Pattern?

Can lead to class bloat if too many trivial strategies are defined.

10. Can Strategy Pattern work with enums?

Yes. Enums with overridden methods can act as compact strategies.