Behavioral Pattern
Template Method
Define the skeleton of an algorithm in a base class and let subclasses override specific steps without changing the algorithm's structure.
Fixed recipe, custom steps.
The Template Method pattern defines the overall steps of an algorithm once, in a base class, but leaves some steps blank for subclasses to fill in. Like a recipe that says "prepare, cook, serve" — the order is fixed, but each dish decides how to cook.
bad.py
# Problem: duplicated recipe with tiny differences per report type. def csv_report(): data = fetch() rows = to_csv(data) write("out.csv", rows) log("done") def pdf_report(): data = fetch() # same steps… rows = to_pdf(data) # …only format differs write("out.pdf", rows) log("done")
good.py
# Fix: Template Method — fixed skeleton, subclasses fill in steps. from abc import ABC, abstractmethod class Report(ABC): def generate(self): # template — do not override lightly data = self.fetch() body = self.format(data) # hook / abstract step self.write(body) self.log("done") def fetch(self): ... def write(self, body): ... def log(self, msg): ... @abstractmethod def format(self, data): """Subclasses customize only this step.""" class CsvReport(Report): def format(self, data): return to_csv(data) class PdfReport(Report): def format(self, data): return to_pdf(data) CsvReport().generate() # same recipe, different format
Hooks and inversion of control.
The base class has a template method that calls a mix of concrete steps, abstract steps (subclasses must implement), and hooks (optional steps with default/empty behavior subclasses may override).
- Hollywood Principle — "don't call us, we'll call you": the base class controls the flow and calls down into subclass steps.
- Code reuse — the invariant algorithm lives in one place; only the varying parts are overridden.
- Enforced structure — often the template method is
finalso the skeleton can't be broken.
Trade-offs and relations.
- vs Strategy — Template Method varies steps via inheritance (compile time, one algorithm skeleton); Strategy varies the whole algorithm via composition (runtime, swappable objects).
- Inheritance downsides — tight base-subclass coupling, fragile base class, and no runtime swapping; favor composition when flexibility matters.
- LSP care — overridden steps must honor the base algorithm's expectations, or the template breaks.
- Real uses — framework lifecycle methods,
AbstractListand other skeletal implementations, unit-test setup/teardown, and servlet/HTTP request handling flows.