skip to content

How does the Template Method pattern differ from the Strategy pattern, and how do you choose between them?

level: middleimportance: must knowfreq 60%

answer

  1. inheritance vs composition
  2. compile-time subclass vs runtime field
  3. one axis vs composable axes
  4. protected state access vs explicit parameters
  5. template method with injected strategies = both

basics

~20 s

Both let part of an algorithm vary. Template Method varies it by inheritance: a subclass overrides a step inside a fixed base-class algorithm. Strategy varies it by composition: you hand the object a separate step-object, which can be swapped at runtime.

solid answer

~50 s

They target the same axis — one part of an algorithm varies — but bind differently. Template Method uses inheritance: the base class owns the skeleton, subclasses override protected steps, and the choice is fixed at compile time by which subclass you instantiate. Strategy uses composition: the varying step lives behind an interface in a separate object that the context holds a reference to, so it can be injected, swapped at runtime, reused across unrelated contexts, and unit-tested standalone. Template Method is cheaper when variation is small, closed and internal — it needs no extra types and the steps can touch protected state. Strategy wins when variation is open-ended, must combine along several axes (single inheritance forces a class explosion in Template Method), must change at runtime, or comes from a different team/module. Practically, Template Method couples subclass to base-class internals (fragile base class); Strategy keeps the seam narrow but requires passing state explicitly across the interface.

code

pseudocode · 13 lines
pseudocode
// Template Method: variant chosen by which subclass you construct
abstract class Exporter {
  final run(rows, sink) { open(sink); rows.each { write(sink, format(it)) }; close(sink) }
  abstract format(row): String
}
class CsvExporter extends Exporter { format(row) = row.id + "," + row.name }

// Strategy: variant is a field, swappable at runtime, reusable
interface RowFormat { format(row): String }
class Exporter2(var fmt: RowFormat) {
  run(rows, sink) { open(sink); rows.each { write(sink, fmt.format(it)) }; close(sink) }
}
val e = Exporter2(CsvFormat()); e.fmt = JsonFormat()   // impossible with the subclass version

go deeper

for a junior

State the core contrast: subclass overrides a step (Template Method) vs. pass in an object that does the step (Strategy). One example each.

for a middle

Add binding time, runtime swappability, testability of the varying step, and the single-inheritance limitation when several aspects vary.

for a senior

Discuss coupling and the fragile base class problem, state-passing costs of Strategy, LSP on overrides, and the hybrid where a final template method delegates to injected collaborators.

for a principal

Frame it as extension-surface policy: which seams you publish, how they version, whether third parties extend by subclassing (hard to evolve) or by implementing a narrow interface (evolvable, mockable, ownable by another team).

## The shared problem Both patterns answer: *"most of this procedure is fixed, one part must differ per case — how do I express that without copy-paste?"* They differ purely in the **binding mechanism**. ## Template Method — variation by inheritance - A base type holds the **template method**: a concrete method containing the ordered skeleton. - The varying part is a **primitive operation** (abstract, must be overridden) or a **hook** (has a default, may be overridden). - You pick a variant by **instantiating a particular subclass**. The binding is decided at construction and cannot change afterwards. - The step runs *inside* the object, so it can freely read protected fields and call other protected helpers. ## Strategy — variation by composition - A **context** object holds a reference to a **strategy** object implementing a small interface (e.g. `Compressor.compress(bytes)`). - The context's algorithm calls `strategy.someStep(...)` where Template Method would call `this.someStep(...)`. - The strategy is supplied from outside — constructor injection, a setter, a factory, configuration, a DI container. - Because it is just a field, it can be **replaced at runtime**, chosen from a map by key, decorated, or combined. ## Head-to-head | Dimension | Template Method | Strategy | |---|---|---| | Mechanism | Inheritance (is-a) | Composition (has-a) | | Binding time | Compile time / instantiation | Runtime, swappable | | Number of types | Fewer (no extra interface) | More (interface + implementations) | | Access to context state | Direct, via protected members | Only what is passed as parameters (or a context object) | | Multiple varying axes | Combinatorial subclass explosion (single inheritance) | Compose several strategies independently | | Reuse of the varying step elsewhere | Hard — it is welded to the hierarchy | Easy — it is a standalone object | | Testability of the step | Needs a subclass instance / base-class setup | Test the strategy directly with plain inputs | | Coupling | Tight: subclass depends on base internals; base changes ripple (fragile base class) | Loose: only the interface is shared | | Who controls flow | Base class calls down (inversion of control) | Context delegates outward | ## Choosing Reach for **Template Method** when: - Variation is *closed* and small — a handful of variants you own, unlikely to grow explosively. - The step genuinely needs privileged access to internal state, and threading that state through an interface would be awkward. - You are building a framework lifecycle and *want* to dictate ordering and guarantee cleanup. - Adding an interface + implementation class per variant would be pure ceremony. Reach for **Strategy** when: - The set of variants is *open* — third parties, plugins, or a growing list. - Selection must happen at runtime (per request, per tenant, from config or a feature flag). - Two or more aspects vary independently (e.g. compression × encryption): Template Method would need a subclass per combination; Strategy composes two fields. - You want the varying logic unit-testable in isolation, reusable outside this algorithm, or owned by a different module. - You are following "favour composition over inheritance" as a default — many teams do, precisely because inheritance publishes internals. ## Nuances people miss - **They compose.** A very common design is a template method skeleton whose steps *delegate to injected strategies*: the base fixes the order, the strategies fix the behaviour. Frameworks do this constantly. - **Functions blur the line.** In a language with first-class functions, a Strategy is often just a lambda parameter, and a template method is a higher-order function taking those lambdas. `runInTransaction { ... }` is a template method with the varying step passed as a function — a Strategy with no interface declaration. - **State passing is the real cost of Strategy.** The step no longer sees the object's internals, so you either widen the strategy's parameter list or pass a context object — sometimes leaking more than the inheritance version did. - **Refactoring direction.** "Replace Inheritance with Delegation" turns a Template Method hierarchy into Strategy; the reverse ("Form Template Method") pulls duplicated procedures in sibling classes up into a shared skeleton. - **LSP still applies.** With Template Method, an override that violates the base class's contract (throws where the skeleton assumes success, mutates state the skeleton owns) breaks substitutability and is hard to detect. With Strategy the contract is a small explicit interface, easier to specify and verify.

  • Can Template Method and Strategy be used together in the same design?
    Yes, and it is a very common framework shape: a final template method fixes the ordering and lifecycle guarantees, while individual steps delegate to collaborator objects injected into the base class. The skeleton stays un-bypassable, and the behaviour stays swappable and independently testable.
  • You have an exporter whose format varies (CSV/JSON/XML) and whose destination varies (file/S3/HTTP). Why does Template Method scale badly here?
    Because the two axes are independent, inheritance forces one subclass per combination — 3 × 3 = 9 classes, and adding a fourth format adds three more. With composition you hold a format strategy and a sink strategy as two fields: 3 + 3 = 6 small classes and any pairing works.
  • What do you lose when you convert a template-method step into an injected strategy?
    Direct access to the object's protected state. The step must now receive everything it needs as parameters or via a context object, which can widen the interface, and you add an interface plus wiring. That's the price of the looser coupling and runtime swappability.

Template Method is a recipe card with one blank you fill in permanently by choosing which laminated card you bought. Strategy is the same recipe with a slot that says "add whichever frosting jar is on the counter" — you can swap the jar mid-service, and the jar works for other cakes too.

saying these in an interview costs you the question

  • "Strategy and Template Method are the same thing" — same intent, fundamentally different binding (composition vs inheritance, runtime vs compile time).
  • "Template Method lets you swap behaviour at runtime" — not without replacing the whole object; the subclass is fixed at construction.
  • Reaching for a class hierarchy when two or more aspects vary independently, causing a combinatorial subclass explosion.
  • Assuming Strategy always needs a named interface and class — a function/lambda parameter is a Strategy.
  • Claiming Strategy is always superior; when variation is closed and needs internal state, the extra indirection is cost without benefit.

context