Strategy Pattern in Java

Naveen AnamaneniUpdated · 5 min read

A food delivery app charges delivery in different ways: a flat fee, free above a minimum order, or extra at peak hours. The checkout code shouldn't care which rule is in force today. The Strategy pattern puts each rule (each "strategy") in its own class behind one interface, and the checkout simply calls the interface. Changing the rule means plugging in a different object, even while the program runs.

Delivery-Fee Strategies

One interface, interchangeable algorithms
Delivery.java▶ Try it
public class Delivery {
    interface DeliveryFee {
        long fee(long orderAmount);
    }
 
    static class FlatFee implements DeliveryFee {
        public long fee(long orderAmount) { return 40; }
    }
 
    static class FreeAbove implements DeliveryFee {
        private final long minimum;
        FreeAbove(long minimum) { this.minimum = minimum; }
        public long fee(long orderAmount) {
            return orderAmount >= minimum ? 0 : 40;
        }
    }
 
    static class PeakHour implements DeliveryFee {
        public long fee(long orderAmount) { return 40 + 30; }
    }
 
    static class Checkout {
        private DeliveryFee feeRule;
 
        Checkout(DeliveryFee feeRule) { this.feeRule = feeRule; }
 
        void setFeeRule(DeliveryFee feeRule) { this.feeRule = feeRule; }
 
        long total(long orderAmount) {
            return orderAmount + feeRule.fee(orderAmount);
        }
    }
 
    public static void main(String[] args) {
        Checkout checkout = new Checkout(new FlatFee());
        System.out.println("Navika, flat fee: Rs. " + checkout.total(350));
 
        checkout.setFeeRule(new FreeAbove(499));
        System.out.println("Srihitha, free above 499: Rs. "
            + checkout.total(620));
 
        checkout.setFeeRule(new PeakHour());
        System.out.println("Advitha, peak hour: Rs. " + checkout.total(350));
 
        checkout.setFeeRule(amount -> amount > 1000 ? 0 : 25);
        System.out.println("Festival rule: Rs. " + checkout.total(1200));
    }
}
Output
Navika, flat fee: Rs. 390
Srihitha, free above 499: Rs. 620
Advitha, peak hour: Rs. 420
Festival rule: Rs. 1200

How it works

  1. DeliveryFee is the strategy interface: one method, one job.
  2. Each rule is a separate class. FreeAbove even has its own setting, the minimum amount.
  3. Checkout holds a DeliveryFee and calls it. It contains no if about which rule applies.
  4. setFeeRule swaps the strategy at runtime; the next total() uses the new rule.
  5. Because DeliveryFee has a single method, a lambda can be a strategy too (lambdas are covered later in the course), handy for one-off rules.

Strategy Removes if-else Chains

Without StrategyWith Strategy
if (rule.equals("FLAT")) ... else if (rule.equals("PEAK")) ... inside CheckoutfeeRule.fee(amount)
A new rule means editing and retesting CheckoutA new rule is a new class
Rules can't be tested on their ownEach strategy is tested separately

Combined with a factory that picks the strategy from configuration ("FLAT", "PEAK"), this is exactly how the discount if-else in Appendix G2 should be replaced: the factory chooses, the strategy calculates.

Common Mistakes

Strategies that need to know about the context's internals. Pass in what they need as arguments; keep the interface small.

A strategy for a rule that never changes. If there will only ever be one rule, a plain method is simpler.

Putting the "which strategy?" if-else back into the context. Move that choice into a factory or configuration.

Interview Corner

What is the strategy pattern?

The strategy pattern defines a family of algorithms, puts each one in its own class implementing a common interface, and makes them interchangeable. A context object holds a reference to a strategy and delegates the work to it, so behavior can be selected or changed at runtime without modifying the context. Comparator passed to sorting methods is the classic Java example, and since Java 8 lambdas make lightweight strategies easy.

What is the difference between the strategy and factory patterns?

Factory is creational: it decides which object to create. Strategy is behavioral: it lets an object's algorithm be swapped. They're often combined: a factory picks which strategy object to use, and the context then calls that strategy.

Practice

  1. Easy: Add a DistanceBased strategy that charges Rs. 8 per kilometer, with the distance passed to its constructor.
  2. Harder: Write FeeRuleFactory.fromCode(String code) that returns the right strategy for "FLAT", "FREE499" and "PEAK", and use it to configure the checkout from a string.

Quick check: Strategy

4 questions
1What does the context object depend on in the Strategy pattern?
2How do you change behavior at runtime?
3Which JDK type is a classic strategy?
4Can a lambda be used as a strategy?

Key Takeaways

Was this tutorial helpful?

Lesson 185 of 482