Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Design Patterns (GoF and modern)  Module 4 – Behavioral patterns: algorithms and requests

Template Method: a fixed skeleton with hooks

A base class fixes the order of an algorithm and subclasses fill in its steps: required steps, optional hooks, the composition alternative and two traps.

  • Beginner
  • 25 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, Java 25, Go 1.27.1, Rust 1.99.0 and Clang 21.0.0
  • By MySmartCoPilot
Free intro, Pro: 7 of 25 minutes free
Free: The idea: the base class owns the order. With a pass: the other 9 sections and 3 interview answers, about 18 minutes.

What you will learn

  • Implement a template method with required steps and optional hooks
  • Rewrite a template method with composition, using strategies or function parameters, and compare the two
  • Recognise template methods in standard libraries and test frameworks
  • Avoid the fragile base class problem and calls to overridable steps from constructors

Before you start

What you’ll learn · Before you start

What you will learn

  • Implement a template method with required steps and optional hooks
  • Rewrite a template method with composition, using strategies or function parameters, and compare the two
  • Recognise template methods in standard libraries and test frameworks
  • Avoid the fragile base class problem and calls to overridable steps from constructors

Before you start

On this page

Template Method is a design pattern in which a base class fixes the steps of an algorithm, and their order, in one method, the template method, and lets subclasses supply the steps that differ. Some steps are required: the base class declares them, and every subclass must implement them. Others are hooks: they come with a default, often one that does nothing, which a subclass may override. The base class calls the subclass’s code, not the other way round, a reversal the Gang of Four call the Hollywood principle: “don’t call us, we’ll call you”. Use it when several variants share one procedure that must not drift apart, such as importers, report builders, test fixtures and the request handling of a framework.

The idea: the base class owns the order

Three importers that each copy the same sequence of reading, checking, converting and saving rows will drift: one skips the check, another converts before checking. A template method keeps the sequence in one place. Here run() is the only method that knows the order; each format supplies how it reads, and one format also uses the hook for rejected rows:

An importer whose run() fixes the order of the steps Python · importer/importer.py
# A data import as a template method: run() fixes the order of the steps once, in the base class. Subclasses fill in
# the steps that differ (reading a format) and may override a hook (here, what to do with rejected rows).
import csv
import io
import json


class Importer:
    def run(self, source):  # the template method: the order lives here and nowhere else
        rows = self.read(source)  # required: every format reads differently
        good = [r for r in rows if self.valid(r)]
        self.rejected(len(rows) - len(good))  # hook: does nothing unless a subclass says otherwise
        saved = [self.transform(r) for r in good]
        print(f"  saved {saved}")

    def read(self, source):
        raise NotImplementedError

    def valid(self, row):  # a default step, shared by all formats
        return row.get("name", "").strip() != "" and row.get("qty", "").isdigit()

    def transform(self, row):
        return (row["name"].strip().title(), int(row["qty"]))

    def rejected(self, count):  # the hook's default
        pass


class CsvImporter(Importer):
    def read(self, source):
        return list(csv.DictReader(io.StringIO(source)))


class JsonImporter(Importer):
    def read(self, source):
        return [{k: str(v) for k, v in item.items()} for item in json.loads(source)]

    def rejected(self, count):  # this format reports bad rows
        print(f"  {count} row(s) rejected")


print("CSV:")
CsvImporter().run("name,qty\npens,12\n,3\nink,x\nruler,4\n")
print("JSON:")
JsonImporter().run('[{"name": "paper", "qty": 500}, {"name": "glue", "qty": "two"}]')

Output

CSV:
  saved [('Pens', 12), ('Ruler', 4)]
JSON:
  1 row(s) rejected
  saved [('Paper', 500)]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 importer.py

Class diagram: Importer has run(), the template method, and its steps; CsvImporter and JsonImporter override only what differs.Importer (base class)+run(source)template method+read(source)required step+valid(row)default step+transform(row)default step+rejected(count)hook, emptyCsvImporter+read(source)csv rowsJsonImporter+read(source)json rows+rejected(count)prints itoverrides read()overrides read()and the hook

Template Method in the importer example: the base class owns the order, subclasses fill in steps

Text description of the diagram

Three class boxes. Each box shows the class name and its operations, with a note on the right of each operation.

At the top is Importer, the base class. Its operations are run(source), the template method; read(source), a required step; valid(row) and transform(row), default steps that subclasses may keep; and rejected(count), a hook whose default does nothing. run() calls the steps in a fixed order: read, then valid for each row, then rejected with the number of bad rows, then transform for each good row.

Below it are two subclasses, each joined to Importer by a solid line with a hollow triangle at Importer, which means inheritance. CsvImporter overrides only read(source), which returns the rows of a CSV text; its line is labelled "overrides read()". JsonImporter overrides read(source), for JSON text, and the hook rejected(count), which prints the count; its line is labelled "overrides read() and the hook". Neither subclass defines run(), so neither can change the order of the steps.

The CSV importer wrote no validation code, yet its rows with an empty name and a quantity of “x” were rejected, because the check belongs to the skeleton. The JSON importer added one behaviour, reporting rejected rows, by overriding the hook, and changed nothing else.

That is the payoff. A fourth format, say fixed-width text, is one new class with one method; the checks, the conversion and the reporting come with the skeleton, and a fix to the validation reaches every format at once. The price is a tight bond between the classes: a subclass can vary only the steps the base class chose to expose, and it relies on the base class calling them in the order it always has.

The pattern comes with a few neighbours worth naming. Composition gives the same skeleton without inheritance: the varying steps are passed in as functions or small objects. Standard-library template methods are classes you already use: Java’s AbstractList and InputStream, Rust’s Iterator, Python’s unittest.TestCase. The fragile base class problem is a subclass that breaks when the base class changes its own skeleton. Calls from constructors to overridable steps run code before the object is ready, and each language fails differently.

Required steps, hooks and a template no subclass can reorder Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • The importer in five more languages, by inheritance and by composition
  • Template methods in standard libraries
  • Two traps reproduced: the fragile base class and constructor calls
  • Template Method or Strategy?
  • Practise: a report generator, two ways
    • A report generator as a template method and by composition
  • Check yourself
  • Check yourself
  • Interview questions
    • A base class with a template method is subclassed by 40 internal services. You need to swap the order of two of its steps for performance. How do you make the change without breaking subclasses you cannot see?
    • In a code review, a change adds a fifth subclass of ReportBase only to format dates differently in one step. What would you suggest, and why?
    • A base class constructor calls an overridable initialise() step. In Java a subclass's initialise() sees null in a field it sets in its own constructor; the same design in C++ runs the base version instead. Explain both, and how you would restructure the code.
  • Key takeaways

7 runnable examples (C++, C++: constructor, Go, Java, Java: AbstractList, Java: InputStream, Java: constructor, Java: final, Python, Rust, Rust: Iterator, TypeScript) · 1 exercise with sample tests · 3 interview answers · about 19 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.