Template Method
Write an algorithm's skeleton once in a base class and let subclasses fill in only the steps that differ.
Big idea
A Template Method is a base-class method that fixes the order of steps in an algorithm and calls overridable step methods for the parts that vary. Subclasses customise steps, never the sequence — so the shared skeleton, with its validation and error handling, is written once.
The problem it solves
Three importers load customers from CSV, JSON, and an old fixed-width mainframe file. Each one: opens the source, parses records, validates them, de-duplicates, saves, and writes a summary. Only parsing differs. Written separately, the three files drift — the CSV importer de-duplicates, the JSON one forgot, the mainframe one validates after saving. A bug fix in the summary has to be made three times.
Think of it like
A recipe card for any bread. Mix, knead, prove, shape, bake, cool — that order never changes. A focaccia card overrides "shape" (flatten, dimple, oil); a baguette card overrides "shape" and "bake" (steam, high heat). Nobody gets to bake before proving.
Structure
- Template method (
run) — the fixed sequence; ideally not overridden. - Abstract steps (
parse) — every subclass must supply them. - Hooks (
validate,after_save) — optional steps with a default that subclasses may override.
In code
import json
class Importer:
def run(self, source): # the template method
records = self.parse(source)
valid = [r for r in records if self.validate(r)]
unique = list({r["email"]: r for r in valid}.values()) # de-dup, always
self.save(unique)
self.after_save(len(unique))
return f"{type(self).__name__}: {len(records)} read, {len(unique)} saved"
def parse(self, source): # abstract step
raise NotImplementedError
def validate(self, record): # hook with a default
return "@" in record.get("email", "")
def save(self, records):
self.saved = records
def after_save(self, count): # hook: does nothing by default
pass
class CsvImporter(Importer):
def parse(self, source):
header, *rows = [line.split(",") for line in source.strip().splitlines()]
return [dict(zip(header, row)) for row in rows]
class JsonImporter(Importer):
def parse(self, source):
return json.loads(source)
def validate(self, record): # stricter rule for this source
return super().validate(record) and bool(record.get("name"))
def after_save(self, count):
print(f" (notified CRM about {count} customers)")
csv_data = "name,email\nAsha,asha@x.io\nBen,not-an-email\nAsha,asha@x.io"
json_data = '[{"name": "Chen", "email": "chen@x.io"}, {"name": "", "email": "anon@x.io"}]'
print(CsvImporter().run(csv_data))
print(JsonImporter().run(json_data))output
CsvImporter: 3 read, 1 saved
(notified CRM about 1 customers)
JsonImporter: 2 read, 1 savedRun and edit it in the app.
The CSV importer never wrote de-duplication or saving; the JSON importer only said what's different about JSON. Both get the same sequence, guaranteed.
What's the difference between an abstract step and a hook in Template Method?
Watch it
The Hollywood principle
"Don't call us, we'll call you." The base class calls the subclass's steps — not the other way round. That inversion is what keeps the sequence in one place, and it's how most frameworks work: you override setUp() / render() / handle(), and the framework decides when to call them.
When to use it — and when not to
Use it when several classes run the same steps in the same order with differences in a few steps, and especially when the order must not be broken (validation before save, cleanup always runs). Prefer Strategy when the varying part should be chosen at runtime or combined with others — inheritance fixes the choice at class-definition time, and each subclass can only have one parent.
Pros and cons
| Pros | Cons |
|---|---|
| Shared sequence written once | Inheritance-based: one variation axis per hierarchy |
| Subclasses override only what differs | Subclasses can be constrained by a rigid skeleton |
| Framework hooks are easy to offer | Many steps make the flow hard to follow ("yo-yo" reading) |
| A subclass that changes a step's meaning can break Liskov |
How it relates
- Factory Method is a template-method step that creates an object.
- Strategy achieves similar variation with composition, chosen at runtime.
- The refactoring Form Template Method (Dealing with Generalization) pulls duplicated sequences from sibling classes into one.
- It's the most common shape for inheritance done well.
Why is the template method itself usually not meant to be overridden?
Key terms
- Template method — A method that defines an algorithm's fixed sequence and calls overridable steps.
- Hook — An optional step with a default implementation that subclasses may override.
- Hollywood principle — Framework code calls your code, not the other way round.