Design Patterns: Behavioral · Behavioral Patterns

Template Method Pattern

Fixing an algorithm's skeleton in a base class while letting subclasses fill in specific steps.

  • 5 min read
  • 12 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The pattern
  3. How it works, step by step
  4. Where you'll see it
  5. Choosing among the behavioral patterns
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Template Method is the behavioral pattern for "the same recipe, different details": a base class fixes the order of an algorithm's steps in one method and lets subclasses fill in the steps that vary. It's how frameworks hand you a skeleton - servlets, test fixtures, abstract collections - and call your code at the right moments. This skill covers the pattern, required steps versus optional hooks, and when composition is the better choice.

You export reports as CSV and as Markdown. Both exporters do the same steps - write a header, write each row, write a footer - but the code is duplicated:

Java

class CsvExporter {
    String export(List<Row> rows) {
        StringBuilder out = new StringBuilder("name,score\n");
        for (Row r : rows) out.append(r.name()).append(',').append(r.score()).append('\n');
        return out.toString();
    }
}
class MarkdownExporter {
    String export(List<Row> rows) {
        StringBuilder out = new StringBuilder("| name | score |\n|---|---|\n");
        for (Row r : rows) out.append("| ").append(r.name()).append(" | ").append(r.score()).append(" |\n");
        return out.toString();
    }
}

Python

class CsvExporter:
    def export(self, rows: list[Row]) -> str:
        out = ["name,score\n"]
        for r in rows:
            out.append(f"{r.name},{r.score}\n")
        return "".join(out)


class MarkdownExporter:
    def export(self, rows: list[Row]) -> str:
        out = ["| name | score |\n|---|---|\n"]
        for r in rows:
            out.append(f"| {r.name} | {r.score} |\n")
        return "".join(out)

When you add sorting, or skipping empty rows, you must remember to change every copy.

We'll go from simple to more technical:

  1. The pattern: a final template method, abstract steps and hooks.
  2. How it works, step by step - "don't call us, we'll call you".
  3. Where you'll see it in the standard libraries and frameworks.
  4. Inheritance costs, and Template Method versus Strategy.

The pattern

Template Method puts the fixed sequence of steps in one method of an abstract base class (the template method) and lets subclasses fill in the steps that vary.

Java

abstract class ReportExporter {
    // The template method: the order of steps is fixed here, once.
    public final String export(List<Row> rows) {
        StringBuilder out = new StringBuilder();
        out.append(header());
        for (Row r : rows) {
            if (includeRow(r)) out.append(formatRow(r));
        }
        out.append(footer());
        return out.toString();
    }

    protected abstract String header();              // must be provided
    protected abstract String formatRow(Row r);      // must be provided
    protected String footer() { return ""; }         // hook: optional
    protected boolean includeRow(Row r) { return true; }   // hook: optional
}

class CsvExporter extends ReportExporter {
    protected String header() { return "name,score\n"; }
    protected String formatRow(Row r) { return r.name() + "," + r.score() + "\n"; }
}

Python

from abc import ABC, abstractmethod
from typing import final


class ReportExporter(ABC):
    # The template method: the order of steps is fixed here, once.
    @final
    def export(self, rows: list[Row]) -> str:
        out = [self.header()]
        for r in rows:
            if self.include_row(r):
                out.append(self.format_row(r))
        out.append(self.footer())
        return "".join(out)

    @abstractmethod
    def header(self) -> str: ...                 # must be provided

    @abstractmethod
    def format_row(self, r: Row) -> str: ...     # must be provided

    def footer(self) -> str:                     # hook: optional
        return ""

    def include_row(self, r: Row) -> bool:       # hook: optional
        return True


class CsvExporter(ReportExporter):
    def header(self) -> str:
        return "name,score\n"

    def format_row(self, r: Row) -> str:
        return f"{r.name},{r.score}\n"

How it works, step by step

  1. Write the algorithm once in the base class, in a method marked final so subclasses can't change the order. (In Python, @typing.final makes a type checker such as mypy reject an override; the interpreter itself doesn't stop one.)
  2. Steps that differ become abstract methods: every subclass must implement them.
  3. Optional steps become hooks: methods with a sensible default that subclasses may override.
  4. Subclasses only fill in the blanks; they never call the template method's steps themselves.
  5. "Don't call us, we'll call you" - the base class calls down into the subclass.

Where you'll see it

  • java.util.AbstractList: implement get() and size(), and you get iteration, contains, indexOf and more for free.
  • java.io.InputStream: subclasses implement one read(); the bulk read(byte[], ...) methods are built on top of it.
  • HttpServlet.service() decides which of doGet() / doPost() to call.
  • Python's collections.abc.Sequence: implement __getitem__ and __len__, and you get in, iteration, index and count for free; unittest.TestCase runs setUp, the test and tearDown in a fixed order.
  • Test frameworks run setup, the test and teardown in a fixed order.

Choosing among the behavioral patterns

If you need to...Reach forIt's about
Let many objects react when something happens, without the source knowing themObserverWho gets told about a change.
Swap one algorithm for another at runtimeStrategyHow a task is done - chosen from outside.
Turn a request into an object you can queue, log, undo or redoCommandWhat to do, packaged for later.
Make an object behave differently depending on its current stateStateBehavior that changes itself as the object moves between states.
Fix the steps of an algorithm but let subclasses fill in some of themTemplate Method (this lesson)The skeleton stays, the details vary.

Common mistakes

MistakeWhat happensDo this instead
A template method that can be overriddena subclass reorders or skips stepsfinal in Java; @typing.final plus a type checker in Python
Ten abstract stepsevery subclass is a chorefew required steps, more hooks with defaults
Hooks that only work if subclasses remember to call superforgotten calls silently skip base workthe template does the base work and calls empty hooks
Using it for variations that must change at run timeone fixed choice per subclassStrategy (composition)
Two independent variations in one hierarchya subclass per combinationinject one of them as a strategy

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why is the template method declared final?

Show answer

So subclasses can't reorder or skip the steps - they may only fill in the steps themselves. The fixed order is the whole guarantee.

2. What's the difference between an abstract step and a hook?

Show answer

Every subclass must implement an abstract step; a hook has a default (often doing nothing) that a subclass may override.

3. When would you pick Strategy over Template Method for the part that varies?

Show answer

When the variant must be swapped at runtime, tested on its own, or combined with other independent variations - composition handles all three; inheritance fixes one choice per subclass.

Key takeaways

  • The base class fixes the order of steps in one final template method.
  • Required steps are abstract; optional steps are hooks with a default.
  • Subclasses fill in blanks - the base class calls them, not the other way round.
  • Great for removing duplicated workflows; watch out for fragile base classes.
  • Composition with callbacks (like JdbcTemplate) is often the more flexible alternative.

Quick revise

The whole skill on one page, for the night before an interview.

Everything from the lesson and the deep dive on one page. Skim it before practice or an interview, and open the full lesson for anything that doesn't ring a bell.

Pick the tool

If you need to...Reach for
A fixed skeleton with blanks, in a frameworkTemplate Method (inheritance)
The same, in application codea template that takes callbacks
Guaranteed setup and cleanup around a stepExecute Around / a context manager
Swap the whole algorithm at run timeStrategy
Two independent variationscallbacks or strategies, not a subclass per combination

Snippets worth knowing by heart

Java

abstract class ReportExporter {
    public final String export(List<Row> rows) {          // the template: fixed order
        StringBuilder out = new StringBuilder(header());
        for (Row r : rows) if (includeRow(r)) out.append(formatRow(r));
        return out.append(footer()).toString();
    }
    protected abstract String header();
    protected abstract String formatRow(Row r);
    protected String footer() { return ""; }                // hook
    protected boolean includeRow(Row r) { return true; }   // hook
}

Python

@contextlib.contextmanager
def transaction(conn):
    conn.begin()                  # fixed: setup
    try:
        yield conn                # the varying step: the with-block body
        conn.commit()
    except Exception:
        conn.rollback()
        raise

The traps

  • An overridable template.
  • Too many abstract steps.
  • Hooks that require super calls.
  • Run-time or independent variations through subclassing.

Staff-level points, one line each

  • Java final is a compile error (overridden method is final); Python @final is only checked by type checkers.
  • Inherited bulk defaults can be slow: InputStream.read(byte[]) made 8,192 read() calls; Sequence.__contains__ scanned 10,000 items.
  • Override bulk/lookup methods when your data can do better.
  • A setUp that skipped super().setUp() lost the base fixture; templates should call empty hooks around required work.
  • JdbcTemplate, Execute Around and context managers are templates with the varying step passed in.
  • Prefer callbacks in application code; keep inheritance for frameworks.

Say it in an interview

  • "The export order lives in one final method; subclasses only format rows."
  • "Footer and filtering are hooks with defaults, so most exporters override two methods."
  • "Our stream overrides the bulk read - the inherited one reads a byte at a time."
  • "Cleanup is the template's job, so callers can't forget it."

Practice

This skill comes with 12 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Template Method Pattern free

Two Importers, One Pipeline

MediumProCoding problemJava · Python

DataImporter.importAll(lines) is a template method that fixes the pipeline for every line: parse it (a required step), validate it, transform it (both hooks with defaults), then store it. Lines that can't be parsed or fail validation are skipped. It ends with a summary.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Template Method Pattern free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime