Strategy & Template Method
Behavioral patterns: strategy formalizes the interchangeable fee policies from Open/Closed; template method fixes an algorithm's skeleton while letting steps vary.
On this page
Two behavioral patterns handle the two ways an algorithm varies. Strategy swaps a whole algorithm at runtime - you already built it in the Open/Closed lesson without the name. Template Method fixes the algorithm's skeleton and lets subclasses vary only specific steps. Knowing which one a situation wants - replace the whole thing, or vary a few steps of a fixed sequence - is the skill.
Strategy: interchangeable whole algorithms
Strategy encapsulates a family of algorithms behind a common interface so they're interchangeable at
runtime. This is exactly the FeePolicy design from Open/Closed, now named:
interface FeePolicy { BigDecimal fee(Transaction t); } // the strategy interface
class FeeCalculator {
private FeePolicy policy; // the current strategy
void setPolicy(FeePolicy p) { this.policy = p; } // swap at runtime
BigDecimal calculate(Transaction t) { return policy.fee(t); }
}The defining trait: the entire algorithm is replaceable, and the choice can change at runtime (per transaction, per config, per A/B test). Strategy favors composition - the context has-a strategy - which is why it's so flexible. It's the go-to when behavior varies along one axis and you want it swappable.
Template Method: a fixed skeleton with variable steps
Sometimes the overall sequence is fixed and only certain steps differ. ledger-legacy generates every report
the same way - open, write header, write rows, write footer, close - but the format of each step varies. That's
Template Method: a base class defines the invariant skeleton in one method and defers the varying steps to
subclasses:
abstract class ReportGenerator {
// the TEMPLATE METHOD - fixed skeleton, not overridable
public final String generate(List<Transaction> txns) {
StringBuilder out = new StringBuilder();
out.append(header()); // step varies
txns.forEach(t -> out.append(row(t))); // step varies
out.append(footer()); // step varies
return out.toString();
}
protected abstract String header(); // subclasses fill these in
protected abstract String row(Transaction t);
protected abstract String footer();
}
class CsvReportGenerator implements/extends ReportGenerator { // only the steps differ
protected String header() { return "date,amount,fee\n"; }
protected String row(Transaction t) { return t.date() + "," + t.amount() + "\n"; }
protected String footer() { return ""; }
}The skeleton (generate) is written once and marked final so no subclass can reorder it; subclasses supply
only header, row, footer. The sequence is guaranteed; the details vary.
Choosing between them
They look similar - both vary behavior - but differ in what varies and how:
| Strategy | Template Method | |
|---|---|---|
| Varies | the whole algorithm | steps within a fixed skeleton |
| Mechanism | composition (has-a) | inheritance (is-a) |
| Swappable at runtime? | yes | no (fixed at compile time by subclass) |
| Coupling | loose | tighter (subclass bound to base) |
Prefer Strategy when you want runtime swapping and looser coupling (the modern default). Reach for Template Method when there's a genuine fixed sequence whose steps vary and the base/subclass relationship is natural - but beware, it uses inheritance, so the LSP cautions apply.
Template Method's modern cousin
You can get Template Method's 'fixed skeleton, pluggable steps' without inheritance by passing the varying steps as functions/lambdas or a small strategy object - composition again. Many teams prefer this: generate(txns, headerFn, rowFn, footerFn) or a ReportFormat strategy. If you find Template Method forcing awkward inheritance, that's the nudge to refactor it into Strategy-style composition.
Template Method is a recipe card with fixed steps - 'prep, cook, plate, garnish, in that order' - where the cook fills in what to prep and how to garnish, but can't reorder the steps or skip plating. Strategy is choosing an entirely different dish for tonight: swap the whole 'make lasagna' algorithm for 'make curry' at will, no shared skeleton imposed. One says 'the sequence is sacred, vary the ingredients'; the other says 'here's a slot - drop in whichever complete method you like.'
ledger-legacy exports statements. Every export follows the same fixed steps (validate → render header → render each row → render footer → checksum), but the rendering format (PDF, CSV, JSON) varies, AND separately the business wants to swap the checksum algorithm (MD5 vs SHA-256) independently at runtime for compliance. How would you structure this?
What's the key difference between Strategy and Template Method?
Key takeaways
- Both are behavioral patterns for varying an algorithm, but along different axes.
- Strategy encapsulates interchangeable whole algorithms behind an interface, composed into a context and swappable at runtime (the FeePolicy design).
- Template Method fixes the algorithm's skeleton in a final base method and defers only specific steps to subclasses.
- Strategy uses composition (loose, runtime-swappable); Template Method uses inheritance (tighter, fixed by subclass) - so LSP cautions apply.
- Prefer Strategy as the modern default; you can also get Template Method's pluggable steps via lambdas/composition instead of inheritance.