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

  • 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 (or final in C++) — clients shouldn't replace the skeleton.
  • Primitive ops are usually protected + pure virtual.
  • Hooks are protected + virtual with 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 final in 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