Strategy Pattern
IntermediateStrategy defines a family of algorithms, encapsulates each one, and makes them interchangeable — eliminating conditional logic.
Overview
The Strategy pattern extracts varying behaviour into separate strategy objects, all implementing a common interface. The context holds a reference to a strategy and delegates the behaviour to it. This replaces large if-else or switch blocks with polymorphism. In modern Java, strategies are often represented as lambdas or method references (a functional interface is the strategy interface), making the pattern extremely lightweight.
Classic Strategy
Define a strategy interface with a single method. Create concrete strategy classes implementing the interface. The context accepts a strategy at construction time or via a setter and delegates to it.
The context does not know which strategy it is using — only that it implements the interface.
// Strategy interface
public interface SortStrategy {
void sort(int[] array);
}
// Concrete strategies
public class QuickSort implements SortStrategy {
@Override
public void sort(int[] array) { /* quicksort impl */ }
}
public class MergeSort implements SortStrategy {
@Override
public void sort(int[] array) { /* mergesort impl */ }
}
// Context
public class DataProcessor {
private SortStrategy strategy;
public DataProcessor(SortStrategy strategy) {
this.strategy = strategy;
}
// Switch strategy at runtime
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void process(int[] data) {
strategy.sort(data);
// further processing...
}
}
// Client
DataProcessor processor = new DataProcessor(new QuickSort());
processor.process(data);
// Switch strategy based on data size
if (data.length > 10_000) {
processor.setStrategy(new MergeSort());
}Strategy with Lambdas (Modern Java)
When the strategy interface has a single abstract method (functional interface), lambdas and method references replace concrete strategy classes entirely. This is the idiomatic Java 8+ approach.
Comparator is the quintessential strategy pattern in the JDK — it encapsulates a comparison algorithm.
// Strategy as functional interface
@FunctionalInterface
public interface DiscountStrategy {
double apply(double price);
}
public class Order {
private double total;
private DiscountStrategy discount;
public Order(double total, DiscountStrategy discount) {
this.total = total;
this.discount = discount;
}
public double finalPrice() {
return discount.apply(total);
}
}
// Lambda strategies — no class needed
DiscountStrategy noDiscount = price -> price;
DiscountStrategy tenPercent = price -> price * 0.90;
DiscountStrategy flatFifty = price -> price - 50.0;
DiscountStrategy memberDiscount = price -> price * 0.80;
Order order1 = new Order(200.0, tenPercent);
Order order2 = new Order(200.0, flatFifty);
System.out.println(order1.finalPrice()); // 180.0
System.out.println(order2.finalPrice()); // 150.0
// Comparator as strategy
List<String> names = List.of("Charlie", "Alice", "Bob");
names.stream()
.sorted(Comparator.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder()))
.forEach(System.out::println);Strategy vs Polymorphism
Strategy differs from simple polymorphism: with polymorphism, behaviour is fixed at class definition via inheritance. With Strategy, behaviour is injected and can change at runtime — the same object can use different algorithms depending on context.
Choose Strategy when: algorithms vary independently from clients, you want to eliminate conditionals, or you want to select algorithms at runtime.
// WITHOUT Strategy — brittle conditional
public double calculateShipping(Order order, String method) {
return switch (method) {
case "STANDARD" -> order.weight() * 0.5;
case "EXPRESS" -> order.weight() * 1.5 + 5.0;
case "OVERNIGHT"-> order.weight() * 3.0 + 10.0;
default -> throw new IllegalArgumentException(method);
};
}
// Adding new method requires modifying this method — violates OCP
// WITH Strategy — open for extension
public interface ShippingStrategy {
double calculate(Order order);
}
// Register strategies in a map
Map<String, ShippingStrategy> strategies = Map.of(
"STANDARD", order -> order.weight() * 0.5,
"EXPRESS", order -> order.weight() * 1.5 + 5.0,
"OVERNIGHT", order -> order.weight() * 3.0 + 10.0
);
// New shipping method = add an entry, no existing code changes
double cost = strategies.get(method).calculate(order);Key Points to Remember
- Strategy encapsulates algorithms behind a common interface, eliminating if-else/switch.
- The context holds a strategy reference and delegates; it does not know the concrete type.
- In Java 8+, functional interfaces + lambdas make Strategy extremely lightweight.
- Comparator<T> is the most-used Strategy in the JDK.
- Strategy enables runtime algorithm selection and satisfies the Open/Closed Principle.
Practice Strategy Pattern in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat problem does the Strategy pattern solve?
How does Java's Comparator interface exemplify the Strategy pattern?
What is the difference between Strategy and State patterns?
How does Strategy satisfy the Open/Closed Principle?
When would you choose Strategy over simple inheritance polymorphism?
Ask Aria about Strategy Pattern
Your personal AI tutor — ask anything about this concept