🏛️ Design Principles & Patterns · Advanced

Strategy in Java

Swappable algorithms, often as lambdas.

🧩 The mysteryYour map app offers fastest, shortest and no-tolls routes. The map never changes; only the routing brain is swapped. You've been using this pattern for years.

Swap the brain, keep the body

Strategy puts each algorithm behind a common interface. The *context* holds a reference to a strategy and delegates to it, so you can swap behavior at runtime without touching the context's code.

interface Route { int minutes(Trip t); }
class Navigator {
    private Route route;
    void use(Route r) { route = r; }
    int eta(Trip t) {
        return route.minutes(t);
    }
}

Lambdas are strategies

In modern Java a strategy is often just a functional interface plus a lambda. Comparator, Predicate and Function are ready-made strategy slots. sort() stays the same; only the comparison you plug in changes.

names.sort(Comparator.comparing(
    String::length));
names.sort(Comparator.naturalOrder());
names.sort(Comparator.reverseOrder());
🔮 Predict it

Same sort, two strategies

What does this print?

var xs = new ArrayList<>(
    List.of("pear", "fig", "apple"));
xs.sort(Comparator.comparing(String::length));
System.out.println(xs);
xs.sort(Comparator.naturalOrder());
System.out.println(xs);
  1. [fig, pear, apple] [apple, fig, pear]
  2. [fig, pear, apple] [pear, fig, apple]
  3. [apple, fig, pear] [fig, pear, apple]
Show the answer

First by length: fig (3), pear (4), apple (5). Then by natural (alphabetical) order: apple, fig, pear. One sort method, two plugged-in algorithms.

Replacing a mode switch

✗ If-chain on a key
int tax(int amt, String c) {
    if (c.equals("BH")) return amt / 10;
    if (c.equals("UK")) return amt / 5;
    return 0;
}

Every new country edits this method, and each rule gets more tangled.

✓ Map of strategies
interface TaxRule { int apply(int amt); }
Map<String, TaxRule> rules = Map.of(
    "BH", a -> a / 10,
    "UK", a -> a / 5);
int tax(int amt, String c) {
    return rules.getOrDefault(c, a -> 0)
        .apply(amt);
}

Each country is its own strategy object, looked up by key.

🤔 Think first

Strategy or Template Method?

A class Order has a private Shipping shipping field, a use(Shipping s) setter, and total() returns 50 + shipping.cost(kg). Which pattern is that, and how would Template Method differ?

Think about it, then reveal the answer

Strategy: Order delegates to a swappable object chosen at runtime (composition). Template Method would instead fix the steps in a superclass and let a subclass override the cost step (inheritance).

⚠️ The trap

The strategy must fit the slot

A method reference only works if its shape matches the interface. Predicate<String> needs a method returning boolean: String::isBlank fits. String::length returns an int and String::trim returns a String, so they don't compile there.

Predicate<String> ok = String::isBlank;
Predicate<String> no = String::length;
// error: bad return type
💼 In the real world

Everyday Strategy

Pricing rules, payment routing, sorting options in a UI, retry policies: all strategies. "Replace conditional with polymorphism" is a classic refactoring and interview topic, and passing a lambda to List.sort is Strategy in one line.

Key takeaways

  1. The context delegates to a strategy interface
  2. Swap behavior without changing the context's code
  3. Comparator, Predicate and Function are ready-made strategy types
  4. Replaces if/switch chains on a 'mode' flag

💡 A GPS app lets you pick fastest, shortest or no-tolls; the map stays the same, only the routing rule changes.

🤯 Did you know?

The Gang of Four book (1994) catalogued 23 design patterns. It lists Strategy's other name as Policy, which is why you'll still see classes like RetryPolicy.

Practice questions

What does this print?

List<String> xs = new ArrayList<>(
    List.of("kiwi", "fig", "banana"));
xs.sort(Comparator.comparing(String::length));
System.out.println(xs);
xs.sort(Comparator.reverseOrder());
System.out.println(xs);
  1. [fig, kiwi, banana] [kiwi, fig, banana]
  2. [fig, kiwi, banana] [banana, fig, kiwi]
  3. [banana, kiwi, fig] [kiwi, fig, banana]
  4. [fig, kiwi, banana] [banana, kiwi, fig]
Check your answer

[fig, kiwi, banana] [kiwi, fig, banana]. First the list is ordered by length (3, 4, 6), then by reverse alphabetical order (k > f > b). Same sort method, different plugged-in strategy.

Which pattern is this?

interface Shipping { int cost(int kg); }
class Order {
    private Shipping shipping;
    void use(Shipping s) { shipping = s; }
    int total(int kg) {
        return 50 + shipping.cost(kg);
    }
}
  1. Template Method
  2. Decorator
  3. Strategy
  4. Observer
Check your answer

Strategy. Order delegates cost calculation to a swappable Shipping object chosen at runtime. Template Method would vary the step through subclassing instead.

Next: one price change, fifty screens update. And a sneaky memory leak hides in the subscriber list.