Template Method Pattern
- Description: Define the skeleton of an algorithm in a base class, defer specific steps to subclasses — invert control so the base orchestrates and the subclass fills in the variable steps.
- My Notion Note ID: K2C-2-21
- Created: 2026-05-22
- Updated: 2026-05-22
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
- 1. The Problem
- 2. Structure
- 3. Classic C++ Example
- 4. Compile-Time Variant with CRTP
- 5. When to Use / When Not To
- 6. Variants and Pitfalls
- 7. Related Patterns
- 8. References
1. The Problem
- Multiple subclasses share the same overall algorithm but differ in a few steps.
- Without the pattern: each subclass copies the wrapper code → duplication, drift, fixes scattered.
- Template Method: base class owns the fixed sequence; subclass overrides the hooks.
- "Don't call us, we'll call you" — the Hollywood Principle. Inversion of control via inheritance.
Examples: report generators (open / format header / format rows / format footer / close), build pipelines, parsers (token / parse / validate / emit), test fixtures (setUp / test / tearDown).
2. Structure
- AbstractClass — defines the template method (the algorithm skeleton) + declares the primitive operations + provides optional hook methods with default no-op behavior.
- ConcreteClass — overrides the primitive ops; may override hooks.
- Template method is usually
non-virtual(orfinalin C++) — clients shouldn't replace the skeleton. - Primitive ops are usually
protected+pure virtual. - Hooks are
protected+virtualwith default empty body.
3. Classic C++ Example
#include <iostream>
#include <string>
#include <vector>
class ReportGenerator {
public:
// Template method — fixed sequence, no overrides.
void generate(const std::vector<std::string>& rows) {
beforeReport();
writeHeader();
for (const auto& r : rows) writeRow(r);
writeFooter();
afterReport();
}
virtual ~ReportGenerator() = default;
protected:
virtual void writeHeader() = 0; // primitive
virtual void writeRow(const std::string& row) = 0; // primitive
virtual void writeFooter() = 0; // primitive
virtual void beforeReport() {} // hook
virtual void afterReport() {} // hook
};
class HtmlReport : public ReportGenerator {
protected:
void writeHeader() override { std::cout << "<table>\n"; }
void writeRow(const std::string& r) override { std::cout << "<tr><td>" << r << "</td></tr>\n"; }
void writeFooter() override { std::cout << "</table>\n"; }
};
class CsvReport : public ReportGenerator {
protected:
void writeHeader() override { std::cout << "row\n"; }
void writeRow(const std::string& r) override { std::cout << r << "\n"; }
void writeFooter() override {}
};
int main() {
HtmlReport h; h.generate({"a", "b"});
CsvReport c; c.generate({"x", "y"});
}
Mark generate final to prevent skeleton override:
void generate(const std::vector<std::string>& rows) final { /* ... */ }
4. Compile-Time Variant with CRTP
Static polymorphism — no vtable, base dispatches via static_cast<Derived*>(this). See CRTP.
#include <iostream>
#include <string>
#include <vector>
template <typename Derived>
class ReportBase {
public:
void generate(const std::vector<std::string>& rows) {
auto& self = static_cast<Derived&>(*this);
self.writeHeader();
for (const auto& r : rows) self.writeRow(r);
self.writeFooter();
}
};
class HtmlReport : public ReportBase<HtmlReport> {
public:
void writeHeader() { std::cout << "<table>\n"; }
void writeRow(const std::string& r) { std::cout << "<tr><td>" << r << "</td></tr>\n"; }
void writeFooter() { std::cout << "</table>\n"; }
};
int main() {
HtmlReport h; h.generate({"a", "b"});
}
Pros: zero overhead, inlinable. Cons: no heterogeneous container; each report type is a distinct template instantiation.
5. When to Use / When Not To
Use when:
- Multiple variants of the same algorithm share a fixed skeleton but differ in a few steps.
- You want to enforce algorithm structure — subclasses can't reorder steps.
- The skeleton is stable; the hooks are the natural extension point.
Avoid when:
- The variation is the whole algorithm, not a step — use Strategy instead.
- Variation needs to be swapped at runtime per call — Strategy fits better; Template Method binds variation to the class.
- Inheritance chain is already deep — adding another base aggravates the fragile-base-class problem.
- Variation is across orthogonal axes — multiple template methods or composition scale better than single inheritance.
6. Variants and Pitfalls
- Hook vs primitive — hooks have defaults (optional override); primitives are pure virtual (mandatory override). Mixing them up causes confusing missing-override compile errors or silent skips.
- Template method should be
finalin C++ — subclasses overriding the skeleton break the whole point. - Calling overridable methods from constructor/destructor — in C++, virtual dispatch resolves to the current class's version during ctor/dtor, not derived. Don't put the template method in the base ctor.
- Fragile base class — changes to the skeleton ripple to all subclasses. Document the contract for each hook (when called, what it can/can't do, ordering guarantees).
- Overuse — three-level deep template-method hierarchies are unreadable. Two levels max in most cases.
- Replacing with composition — when hooks proliferate, the class is asking to be split into a context + several Strategy objects.
7. Related Patterns
- Template Method vs Strategy — both vary a step. Template Method: inheritance, hooks overridden in subclasses, structure fixed at compile time per type. Strategy: composition, algorithm injected as an object, switchable at runtime. Same intent, different mechanism: Template Method swaps via class hierarchy; Strategy swaps via field assignment.
- CRTP variant — see § 4 and CRTP. Static-polymorphic Template Method for performance-critical code.
- Factory Method — frequently is a Template Method step ("create the product"). GoF defines Factory Method as a specialization of Template Method.
- Hollywood Principle ("don't call us, we'll call you") — Template Method is the canonical OO example; same principle drives DI containers and event-driven frameworks.
8. References
- Gamma, Helm, Johnson, Vlissides. Design Patterns. Template Method chapter.
- Wikipedia: Hollywood Principle
- isocpp.org Core Guidelines: C.138
- Coplien, Curiously Recurring Template Pattern (C++ Report, 1995) — CRTP origin.