AcademyDesign Patterns
Template Method pattern
Fixing the step order of an algorithm in a base class while subclasses fill in the rest, the subclassing it costs to change one step, and a base class open to rewrites.
6 min readUpdated 3 exercises
Template Method makes the opposite trade from Strategy: instead of swapping a whole algorithm from outside, it fixes the algorithm's shape and lets a subclass fill in one or two of its steps from inside.
A Template Method defines the fixed order a multi-step algorithm runs in, and leaves one or two of those steps abstract for a subclass to fill in. It's the opposite trade from Strategy: Strategy swaps the whole algorithm from outside; Template Method fixes the algorithm's shape from inside and lets subclasses vary a specific step.
The problem it solves
You export reports. Every export does the same three things in the same order: fetch the data, format it, save it. The only difference between a CSV export and a JSON export is the formatting step.
If each exporter is written separately, the shared steps are copied:
- Duplicated code. The fetch and save logic exists in every exporter, and a bug fix has to be repeated.
- The order can drift. One copy formats before validating, another after.
- New formats mean a new copy. Adding XML means duplicating the skeleton again.
The fix is to write the skeleton once in a base class, and leave only the varying step for subclasses to provide.
Structure
Two parts:
- The abstract class: contains the template method (
export()), which calls the steps in order. Common steps are implemented here, varying steps are abstract or have defaults. - Concrete classes (
CsvExporter,JsonExporter): override only the steps that differ.
class ReportExporter {
export() {
const data = this.fetchData();
const formatted = this.format(data);
return this.save(formatted);
}
fetchData() { return ['a', 'b', 'c']; }
save(formatted) { return 'saved:' + formatted; }
}
class CsvExporter extends ReportExporter {
format(data) { return data.join(','); }
}
class JsonExporter extends ReportExporter {
format(data) { return JSON.stringify(data); }
}
// new CsvExporter().export() → 'saved:a,b,c'
// new JsonExporter().export() → 'saved:["a","b","c"]'export() is the template and is never overridden. It always fetches, then formats, then saves. A subclass cannot change the order, only what format does. That is the guarantee: the algorithm's shape stays the same everywhere.
Where it is used in real systems
| Use case | The fixed skeleton | The steps you fill in |
|---|---|---|
| Test frameworks | Setup, run, teardown | beforeEach, the test body, afterEach |
| Data pipelines | Extract, transform, load | The transform for each source |
| Report and file exporters | Fetch, format, save | The format |
| Framework lifecycles | Mount, update, unmount | Your component's hooks |
| Request handling | Authenticate, validate, handle, respond | The handler |
| Game loops | Input, update, render | Each game's update |
| Build tools | Parse, transform, emit | A plugin's transform |
Template Methods you already use
- React class components. The framework calls
componentDidMount,renderandcomponentWillUnmountin a fixed order, and you supply the steps. - Vue and Angular lifecycle hooks.
onMounted,ngOnInitand friends are steps you fill into a lifecycle the framework owns. - Jest and Vitest.
beforeEach,testandafterEachare slots in a fixed skeleton. - Java servlets.
HttpServlet.service()callsdoGetordoPostdepending on the request. - NestJS lifecycle hooks such as
onModuleInitandonApplicationShutdown.
This is often called the Hollywood principle: don't call us, we'll call you. The framework calls your code at the right moment, and you do not call the framework.
Hooks
Besides mandatory steps, a template can offer hooks: optional steps with a default that does nothing. A subclass overrides one only if it needs to.
abstract class ReportExporter {
export(): string {
const data = this.fetchData();
this.beforeSave(data); // hook, empty by default
return this.save(this.format(data));
}
protected beforeSave(_data: string[]): void {}
protected abstract format(data: string[]): string;
protected fetchData(): string[] {
return ['a', 'b', 'c'];
}
protected save(formatted: string): string {
return 'saved:' + formatted;
}
}Template Method or Strategy
| Template Method | Strategy | |
|---|---|---|
| Mechanism | Inheritance | Composition |
| What varies | One step inside a fixed algorithm | The whole algorithm |
| When it is chosen | At class definition | At runtime, can change |
| Coupling | Subclass tied to its parent | Loose |
Modern advice is to prefer composition, so Strategy is often the first choice. Template Method fits when the skeleton really is fixed and frameworks own the control flow. In JavaScript you can also pass the varying step in as a function, which turns a Template Method into a Strategy.
When not to use it
- Inheritance is a poor fit. Subclasses are tightly coupled to the base class, and a change to the skeleton can break every one of them.
- Many variations. With many steps to override, the hierarchy grows and becomes hard to follow.
- The skeleton is not really fixed. If subclasses keep needing to change the order, the template is wrong.
- You need runtime switching. A subclass is chosen when the object is created. Use Strategy to swap later.
- A function argument is enough. In JavaScript,
export(format)with a function is simpler than a class hierarchy.
What interviewers ask
- "What is the Template Method pattern?" A base class defines the skeleton of an algorithm and subclasses fill in specific steps.
- "Template Method versus Strategy?" Inheritance and a fixed skeleton, versus composition and swappable whole algorithms.
- "What is the Hollywood principle?" The framework calls your code, and your code does not call the framework.
- "What are hooks?" Optional steps with a default, overridden only when needed.
- "Give a real example." Test lifecycle methods, framework component lifecycles, or a data pipeline.
The mistake to avoid
A subclass that overrides the template method itself. If CsvExporter redefines export(), the guarantee that every exporter follows the same order is gone, and the shared skeleton no longer means anything. Keep the template method final where the language allows it, and override only the steps.
The mechanism above, a base class calling methods that subclasses fill in, is the same in JavaScript, Python or Java. Only the syntax changes.
Now practice it
1 challenge · 2 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- Template MethodProFix an algorithm's step order in a base class and vary exactly one step in a subclass — the fixed skeleton this page walks through, made runnable.ChallengeSolve · MediumChallengeSolve · Medium
- Design Patterns · Behavioral PatternsTen quick questions across Strategy, State, Observer, Command and the rest of the behavioral family, to check the mental model before this one.QuizTake · HardQuizTake · Hard
- Design Patterns · Architectural PatternsProA second, tougher pass on the same behavioral family, for once the first set feels easy.QuizTake · HardQuizTake · Hard
Read next
Strategy pattern
Swapping an algorithm without touching the class that uses it, the interface every strategy must implement even for a one-off, and reaching for it before a second case exists.
6 min
State pattern
Letting an object swap what it can do next, decided from inside itself, the state class and transition table it costs, and two states that disagree who owns a transition.
6 min