Template Method

Learning Objectives

Define an algorithm skeleton with customizable steps (hooks).

Compare Template Method with Strategy and composition.

Prerequisites: Modules 7, 40.

Intuition and Definition

Intent: define the skeleton of an algorithm in a base-class method, deferring some steps to subclasses, so subclasses can redefine steps without changing the algorithm's structure.

Participants: AbstractClass with a non-virtual template method that calls primitive operations (pure virtual) and hooks (virtual with default behaviour); ConcreteClasses override the steps.

In C++ this is often written with the Non-Virtual Interface (NVI) idiom: public non-virtual functions call private virtual ones.

Why It Matters

Many processes share the same order of steps (validate → prepare → execute → report) but differ in a few details. Template Method fixes the order in one place.

Syntax and Language Notes

// Java (not run): final template method, protected abstract steps
public abstract class SelfTest {
    public final boolean run() { setUp(); boolean ok = check(); tearDown(); return ok; }
    protected abstract boolean check();
    protected void setUp() {} protected void tearDown() {}
}

Complete Example

#include <iostream>
#include <string>

class SelfTest {
public:
    virtual ~SelfTest() = default;
    bool run() {                                       // template method (non-virtual: order is fixed)
        std::cout << "[" << name() << "]\n";
        prepare();
        bool ok = check();
        std::cout << "  result: " << (ok ? "PASS" : "FAIL") << "\n";
        cleanup();
        return ok;
    }
private:
    virtual std::string name() const = 0;              // primitive operations
    virtual bool check() = 0;
    virtual void prepare() { std::cout << "  prepare: default\n"; }   // hooks with defaults
    virtual void cleanup() {}
};

class RamTest : public SelfTest {
    std::string name() const override { return "RAM march test"; }
    bool check() override { std::cout << "  writing and reading patterns 0x55/0xAA\n"; return true; }
};

class CanLoopbackTest : public SelfTest {
    std::string name() const override { return "CAN loopback"; }
    void prepare() override { std::cout << "  prepare: controller into loopback mode\n"; }
    bool check() override { std::cout << "  frame 0x123 sent, nothing received\n"; return false; }
    void cleanup() override { std::cout << "
…
[RAM march test]
  prepare: default
  writing and reading patterns 0x55/0xAA
  result: PASS
[CAN loopback]
  prepare: controller into loopback mode
  frame 0x123 sent, nothing received
  result: FAIL
  cleanup: controller back to normal mode
1/2 tests passed

Step-by-Step Walkthrough

run() fixes the order: header → prepare → check → report → cleanup. Subclasses can't reorder it.

RamTest overrides only the required steps; CanLoopbackTest also overrides the hooks.

The virtual functions are private (NVI): subclasses override them, but only the base class calls them.

Common Mistakes

Making the template method virtual — subclasses can then break the skeleton.

Too many hooks — the base class becomes a configuration maze; consider Strategy.

Deep hierarchies of template classes where each level overrides different steps.

Trade-offs and Alternatives

Template Method uses inheritance (fixed at compile time, one combination per class). Strategy uses composition (pass step objects/functions; combine at run time). Prefer Strategy when steps vary independently or need to be mixed and matched.

Real-World Use

Unit test frameworks (setUp/test/tearDown), servlet service() → doGet(), game loops (update/render), report generators, AUTOSAR/embedded self-test frameworks, the NVI idiom throughout C++ libraries.

Practice

Q1. (Easy) Is the template method itself usually virtual?

Answer: No — it's non-virtual (or final) so the algorithm structure is fixed.

Q2. (Medium) What is a hook?

Answer: An overridable step with a default (often empty) implementation.

Q3. (Advanced) Rewrite SelfTest so the steps are passed as functions (Strategy/composition) instead of overridden.

#include <functional>
#include <iostream>
#include <string>
struct SelfTest {
    std::string name;
    std::function<bool()> check;
    std::function<void()> prepare = [] {};
    std::function<void()> cleanup = [] {};
    bool run() const {
        prepare();
        bool ok = check();
        cleanup();
        std::cout << name << ": " << (ok ? "PASS" : "FAIL") << "\n";
        return ok;
    }
};
int main() {
    SelfTest ram{"RAM", [] { return true; }};
    SelfTest can{"CAN", [] { return false; }, [] { std::cout << "loopback on\n"; }, [] { std::cout << "loopback off\n"; }};
    ram.run();
    can.run();
}
RAM: PASS
loopback on
loopback off
CAN: FAIL

Interview Question

Q1. "Template Method or Strategy?" Answer: Both vary steps of an algorithm. Template Method uses inheritance: the base class owns the skeleton, subclasses override steps, the combination is fixed per subclass, and subclasses are coupled to the base. Strategy uses composition: the algorithm calls step objects supplied at run time, which can be combined freely and tested alone. Choose Template Method for a stable skeleton with one or two variation points in a framework you control; choose Strategy when steps vary independently or at run time.

Quick Quiz

Q1. Which C++ idiom implements Template Method with private virtuals?

Answer: NVI (Non-Virtual Interface).

Q2. Template Method relies on…?

Answer: Inheritance and overriding.

Q3. Name a framework example.

Answer: Unit-test setUp/tearDown around each test.

Summary

Template Method fixes an algorithm's order and lets subclasses fill in steps.

Prefer Strategy when steps vary independently.

Related: Modules 28, 40.