The Strategy Pattern in Java
Software developers often face situations where an application needs to perform the same task in different ways depending on context. Hard-coding these variations with multiple if-else or switch statements can quickly make code difficult to maintain and extend.
This is where the Strategy Pattern becomes valuable.
What is the Strategy Pattern?
The Strategy Pattern is a behavioral design pattern that allows you to define a family of algorithms, encapsulate each one, and make them interchangeable at runtime.
Instead of embedding multiple algorithms directly into a class, you separate them into individual strategy classes and allow the application to choose which one to use.
Simple Definition
The Strategy Pattern enables selecting an algorithm or behavior at runtime without changing the code that uses it.
Why Use the Strategy Pattern?
Without the Strategy Pattern, code often looks like this:
public class PaymentService {
public void processPayment(String paymentType, double amount) {
if ("CREDIT_CARD".equals(paymentType)) {
System.out.println("Processing credit card payment");
} else if ("PAYPAL".equals(paymentType)) {
System.out.println("Processing PayPal payment");
} else if ("BANK_TRANSFER".equals(paymentType)) {
System.out.println("Processing bank transfer payment");
}
}
}
Every time a new payment method is introduced, the class must be modified.
This violates the Open/Closed Principle, which states that software entities should be open for extension but closed for modification.
Structure of the Strategy Pattern
The Strategy Pattern consists of three main components:
1. Strategy Interface
Defines the common contract for all algorithms.
public interface PaymentStrategy {
void pay(double amount);
}
2. Concrete Strategies
Implement specific algorithms.
Credit Card Strategy
public class CreditCardPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid €" + amount + " using Credit Card");
}
}
PayPal Strategy
public class PaypalPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid €" + amount + " using PayPal");
}
}
Bank Transfer Strategy
public class BankTransferPayment implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid €" + amount + " using Bank Transfer");
}
}
3. Context
Uses a strategy without knowing its implementation details.
public class PaymentContext {
private PaymentStrategy paymentStrategy;
public PaymentContext(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void processPayment(double amount) {
paymentStrategy.pay(amount);
}
}
Using the Pattern
public class Main {
public static void main(String[] args) {
PaymentContext context =
new PaymentContext(new CreditCardPayment());
context.processPayment(100.0);
context =
new PaymentContext(new PaypalPayment());
context.processPayment(50.0);
}
}
Output:
Paid €100.0 using Credit Card
Paid €50.0 using PayPal
Notice that the PaymentContext never changes. Only the strategy changes.
Real-World Examples
The Strategy Pattern appears everywhere in modern software systems.
Payment Processing
- Credit Card
- PayPal
- Apple Pay
- Google Pay
- Bank Transfer
Each payment method becomes a strategy.
Sorting Algorithms
An application may choose:
- Quick Sort
- Merge Sort
- Bubble Sort
depending on data size and performance requirements.
Shipping Cost Calculation
Different shipping providers can implement:
- Standard Shipping
- Express Shipping
- International Shipping
as separate strategies.
Authentication Providers
Applications may support:
- Username/Password
- Google Login
- Microsoft Login
- GitHub Login
Each authentication mechanism can be implemented as a strategy.
Strategy Pattern with Java Lambdas
Modern Java allows us to simplify the Strategy Pattern using functional interfaces.
Functional Interface
@FunctionalInterface
public interface DiscountStrategy {
double applyDiscount(double price);
}
Using Lambdas
DiscountStrategy regular =
price -> price;
DiscountStrategy silver =
price -> price * 0.90;
DiscountStrategy gold =
price -> price * 0.80;
System.out.println(gold.applyDiscount(100));
Output:
80.0
This approach reduces boilerplate while retaining the benefits of the pattern.
Advantages
| Cleaner Code | Eliminates large conditional blocks. |
| Easier Maintenance | Each algorithm lives in its own class. |
| Open for Extension | Adding new strategies requires no changes to existing code. |
| Better Testability | Each strategy can be tested independently. |
| Runtime Flexibility | Behavior can change dynamically during execution. |
Disadvantages
| More Classes | Applications may end up with many small strategy classes. |
| Additional Complexity | For simple scenarios, the pattern may feel like overengineering. |
| Client Awareness | The client often needs to know which strategy to choose. |
Strategy Pattern vs State Pattern
These two patterns are often confused.
| Strategy Pattern | State Pattern |
|---|---|
| Client chooses behavior | Object changes behavior based on internal state |
| Focuses on interchangeable algorithms | Focuses on state transitions |
| Strategy selection is external | State changes are internal |
When Should You Use It?
Consider the Strategy Pattern when:
✅ You have multiple ways of performing the same task.
✅ You frequently add new business rules.
✅ You want to avoid large if-else or switch statements.
✅ You need to change behavior dynamically at runtime.
Avoid it when:
❌ There is only one algorithm.
❌ The behavior is unlikely to change.
❌ Simplicity is more important than flexibility.
Final Thoughts
The Strategy Pattern is one of the most practical design patterns in object-oriented programming. It promotes clean architecture, improves maintainability, and aligns well with SOLID principles—especially the Open/Closed Principle.
Whether you’re building payment systems, authentication frameworks, pricing engines, or cloud-native applications, the Strategy Pattern provides a flexible and scalable way to manage varying business behaviors.
The next time you find yourself writing a long chain of if-else statements, consider whether those branches are actually different strategies waiting to be extracted into their own classes.

