Template Method Pattern
Concept
Template Method defines the fixed skeleton of an algorithm in a base class method — the sequence of steps and the order they run in — while deferring one or more individual steps to subclasses via overridable methods (often called "hooks" or, in Java-style parlance, protected abstract methods). The base class's method is typically marked final/non-overridable so the order can never change, while the subclass only ever supplies the content of specific steps. A classic shape: process() calls readInput(), validate(), transform(), writeOutput() in that fixed order; a CsvImporter and a JsonImporter subclass each override readInput() and transform() but share validate() and writeOutput() verbatim from the base class.
The defining trait that separates Template Method from a bag of shared helper functions is inversion of control at the class level: the base class calls into the subclass's overrides, not the other way around — "don't call us, we'll call you." This is sometimes called the Hollywood Principle. It's also one of the most naturally-occurring GoF patterns in framework code specifically because frameworks need exactly this shape: a test framework's setUp() → runTest() → tearDown() sequence, a web framework's request-handling lifecycle (before_request → dispatch → after_request), or an ORM's save flow (validate() → beforeSave() → persist() → afterSave()) are all Template Method whether or not the library calls it that.
Tradeoffs
| Approach | Benefit | Cost |
|---|---|---|
| Template Method (base class fixes the algorithm's shape, subclasses fill in steps) | Algorithm structure lives in exactly one place and can't drift between subclasses; adding a new variant means implementing only the varying steps | Requires inheritance, which couples the subclass to the base class's exact step names/signatures — changing the skeleton's shape is a breaking change for every subclass |
| Strategy (whole algorithm swapped as one unit via composition) | No inheritance coupling; a strategy can be swapped at runtime, not just at subclassing time | Doesn't help when most of the algorithm is shared and only a couple of steps vary — a full Strategy per variant duplicates the shared parts unless the shared parts are also factored out separately |
| Copy-paste per variant (each variant is its own top-to-bottom implementation) | No abstraction to design; each variant is independently readable in isolation | The shared steps drift over time — a bugfix or behavior change applied to one variant's copy of validate() doesn't propagate to the others, since there's no single source of truth |
Template Method and Strategy solve adjacent but distinct problems: Template Method varies some steps of an otherwise-fixed sequence via inheritance; Strategy swaps an entire algorithm via composition. When most of a Template Method's steps end up being overridden by every subclass, that's usually a sign the fixed skeleton isn't actually shared enough to justify the pattern, and Strategy (or just distinct top-level functions) is the better fit.
When to use / when not to
- Use when several variants of a process share the same overall sequence of steps, and the sequence itself should never be reorderable by a subclass — data import/export pipelines, test framework lifecycles, request-handling middleware chains, build/release pipeline stages.
- Especially valuable for enforcing a fixed compliance-relevant order — e.g. a payment-processing base class where
validateFunds()must always run beforechargeCard(), and no subclass should be able to accidentally reorder or skip that. - Don't reach for it when the variation is in one step only and the language has first-class functions — a single injected callback/closure often does the same job with composition instead of inheritance.
- Avoid deep Template Method hierarchies (a subclass of a subclass of a subclass, each overriding different steps) — the actual runtime behavior for any given leaf class becomes hard to read without jumping across multiple files; two levels is usually the practical ceiling.
Common pitfall
Overriding a "hook" step in a way that silently violates an invariant the base class's other steps assume holds — e.g. a subclass's transform() override returns null on an edge case, and the base class's writeOutput() step, written assuming transform() always returns a valid object, throws a null-pointer/attribute error several stack frames away from the actual mistake. Because the base class controls the call sequence but not the subclass's internals, template-method bugs often surface far from their cause. The fix is documenting each hook's contract explicitly (what it must return, what invariants it must preserve) as part of the base class, not just trusting subclass authors to infer it from reading sibling implementations.
Engineering Lens
Template Method is a useful lens for spotting duplicated business logic that's been copy-pasted across sibling classes instead of factored into a shared skeleton — a code review red flag is three implementations of "the same" process where 80% of the lines are identical and only a couple of steps differ; that's Template Method's problem shape, whether or not anyone reaches for the name. The pattern also plays a specific defensive role in security- or compliance-sensitive pipelines: putting the ordering-critical steps in a non-overridable base method (rather than trusting every implementation to call them in the right order itself) turns "developer remembered to validate before charging" from a per-implementation hope into a structural guarantee.
Related
- Strategy Pattern — Strategy swaps a whole algorithm via composition; Template Method varies individual steps of a fixed algorithm via inheritance