Skip to content
FollowtheDamnCode

Template Method

Write an algorithm's skeleton once in a base class and let subclasses fill in only the steps that differ.

Big idea

A Template Method is a base-class method that fixes the order of steps in an algorithm and calls overridable step methods for the parts that vary. Subclasses customise steps, never the sequence — so the shared skeleton, with its validation and error handling, is written once.

The problem it solves

Three importers load customers from CSV, JSON, and an old fixed-width mainframe file. Each one: opens the source, parses records, validates them, de-duplicates, saves, and writes a summary. Only parsing differs. Written separately, the three files drift — the CSV importer de-duplicates, the JSON one forgot, the mainframe one validates after saving. A bug fix in the summary has to be made three times.

Think of it like

A recipe card for any bread. Mix, knead, prove, shape, bake, cool — that order never changes. A focaccia card overrides "shape" (flatten, dimple, oil); a baguette card overrides "shape" and "bake" (steam, high heat). Nobody gets to bake before proving.

Structure

The skeleton is fixed; some steps are hooks
  • Template method (run) — the fixed sequence; ideally not overridden.
  • Abstract steps (parse) — every subclass must supply them.
  • Hooks (validate, after_save) — optional steps with a default that subclasses may override.

In code

Two importers, one pipeline
import json

class Importer:
    def run(self, source):                     # the template method
        records = self.parse(source)
        valid = [r for r in records if self.validate(r)]
        unique = list({r["email"]: r for r in valid}.values())   # de-dup, always
        self.save(unique)
        self.after_save(len(unique))
        return f"{type(self).__name__}: {len(records)} read, {len(unique)} saved"

    def parse(self, source):                   # abstract step
        raise NotImplementedError
    def validate(self, record):                # hook with a default
        return "@" in record.get("email", "")
    def save(self, records):
        self.saved = records
    def after_save(self, count):               # hook: does nothing by default
        pass


class CsvImporter(Importer):
    def parse(self, source):
        header, *rows = [line.split(",") for line in source.strip().splitlines()]
        return [dict(zip(header, row)) for row in rows]

class JsonImporter(Importer):
    def parse(self, source):
        return json.loads(source)
    def validate(self, record):                # stricter rule for this source
        return super().validate(record) and bool(record.get("name"))
    def after_save(self, count):
        print(f"  (notified CRM about {count} customers)")


csv_data = "name,email\nAsha,asha@x.io\nBen,not-an-email\nAsha,asha@x.io"
json_data = '[{"name": "Chen", "email": "chen@x.io"}, {"name": "", "email": "anon@x.io"}]'
print(CsvImporter().run(csv_data))
print(JsonImporter().run(json_data))
output
CsvImporter: 3 read, 1 saved
  (notified CRM about 1 customers)
JsonImporter: 2 read, 1 saved

Run and edit it in the app.

The CSV importer never wrote de-duplication or saving; the JSON importer only said what's different about JSON. Both get the same sequence, guaranteed.

What's the difference between an abstract step and a hook in Template Method?

Watch it

The Hollywood principle

"Don't call us, we'll call you." The base class calls the subclass's steps — not the other way round. That inversion is what keeps the sequence in one place, and it's how most frameworks work: you override setUp() / render() / handle(), and the framework decides when to call them.

When to use it — and when not to

Use it when several classes run the same steps in the same order with differences in a few steps, and especially when the order must not be broken (validation before save, cleanup always runs). Prefer Strategy when the varying part should be chosen at runtime or combined with others — inheritance fixes the choice at class-definition time, and each subclass can only have one parent.

Pros and cons

ProsCons
Shared sequence written onceInheritance-based: one variation axis per hierarchy
Subclasses override only what differsSubclasses can be constrained by a rigid skeleton
Framework hooks are easy to offerMany steps make the flow hard to follow ("yo-yo" reading)
A subclass that changes a step's meaning can break Liskov

How it relates

Why is the template method itself usually not meant to be overridden?

Key terms

  • Template method — A method that defines an algorithm's fixed sequence and calls overridable steps.
  • Hook — An optional step with a default implementation that subclasses may override.
  • Hollywood principle — Framework code calls your code, not the other way round.