Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Template Method pattern puts an algorithm’s fixed sequence in one base-class method and delegates selected steps to subclasses. In Java, an abstract class usually contains that orchestration method, concrete invariant steps, abstract operations for required variation, and protected hooks for optional variation. Subclasses change those extension points without replacing the sequence.

What the Template Method pattern does

The pattern’s defining idea is to “define the skeleton of an algorithm in an operation, deferring some steps to subclasses.” The base class controls when each step runs; subclasses control selected details of how a step is performed.

For example, every report import might need to validate input, read records, transform them, and write output. The order is part of the import process. A CSV importer and an API importer can provide different reading or transformation code while following the same lifecycle.

Recognize the structure

The template method

This is the method that owns the algorithm’s sequence. It calls the steps in a deliberate order and is commonly public. Marking it final prevents subclasses from reordering, omitting, or inserting calls when that sequence is a contract.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Invariant operations

These steps are shared by every variant, so the base class implements them directly. They can be private or final methods when subclasses should not alter them.

Primitive operations

Primitive operations are required extension points. An abstract method communicates that every concrete subclass must provide an implementation. Use this for a step for which the base class has no sensible universal behavior.

Hooks

A hook is an overridable method with a default implementation, often an empty method or a default decision. Subclasses may override it, but they do not have to. Hooks are appropriate when most variants share the default behavior.

Extension point Base-class behavior Subclass obligation Typical use
Abstract primitive operation No implementation Must implement Reading a source whose format differs for every importer
Concrete invariant operation Shared implementation Normally cannot change it Validation rules common to all imports
Hook Provides a default May override Optional logging, filtering, or notification

A compact Java example

This example prepares reports. Validation and output formatting are fixed; loading and transforming data are required operations; an optional hook can add a header.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import java.util.List;

abstract class ReportJob {
    // The algorithm skeleton: its order is the contract.
    public final void run() {
        validateInput();
        List<String> records = readRecords();
        List<String> transformed = transform(records);
        addHeader(transformed);       // optional hook
        writeOutput(transformed);
    }

    private void validateInput() {
        System.out.println("Input is valid");
    }

    protected abstract List<String> readRecords();

    protected abstract List<String> transform(List<String> records);

    protected void addHeader(List<String> records) {
        // Default: no header.
    }

    private void writeOutput(List<String> records) {
        records.forEach(System.out::println);
    }
}

final class CsvReportJob extends ReportJob {
    @Override
    protected List<String> readRecords() {
        return List.of("alice,42", "bob,37");
    }

    @Override
    protected List<String> transform(List<String> records) {
        return records.stream()
                .map(row -> row.replace(',', ':'))
                .toList();
    }

    @Override
    protected void addHeader(List<String> records) {
        records.add(0, "name:score");
    }
}

Calling new CsvReportJob().run() always starts with validation, then reads, transforms, adds the optional header, and writes. The base-class call to readRecords(), transform(), and addHeader() uses normal Java dynamic dispatch, so the subclass implementations execute at runtime.

The sample uses List.of, which returns an unmodifiable list. If the hook must insert a header, return a mutable list from readRecords() or create one in transform(); otherwise the insertion will fail with UnsupportedOperationException. For example, return new ArrayList<>(List.of(...)) and import java.util.ArrayList.

Why make the template method final?

final is a design choice, not a requirement of the pattern. Use it when callers depend on the exact sequence—for example, validation must precede any I/O, or cleanup must always happen after processing. Leaving the method overridable can be reasonable when subclasses are intentionally allowed to extend or replace the workflow, but that weakens the guarantee that every variant follows the same algorithm.

Required operations versus hooks

Choose an abstract operation when omitting an implementation would make the algorithm incomplete or unsafe. The compiler then exposes an incomplete subclass immediately.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose a hook when a neutral default is valid. An empty hook is suitable for optional notifications; a boolean hook can supply a default policy:

protected boolean shouldPublish(List<String> records) {
    return true;
}

Keep hooks narrow and document when they run, what state is available, and whether changing arguments is allowed. A hook that silently changes core invariants makes the base class difficult to reason about.

Java’s AbstractList as a skeletal-implementation example

Oracle’s Java SE 26 API documentation describes java.util.AbstractList<E> as a skeletal implementation that reduces the work required to implement List. For an unmodifiable list, a subclass supplies get(int) and size(). A modifiable, variable-size list additionally overrides set(int, E), add(int, E), and remove(int) as needed. The class supplies iterator and list-iterator behavior on top of the random-access operations.

This is best described as a practical skeletal implementation illustrating Template Method: shared operations are organized around behavior supplied by a subclass, even though the API documentation does not label AbstractList itself as “the Template Method pattern.”

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When inheritance is a good fit

Before creating an abstract base class, evaluate the process and its variation:

  • Stable sequence: The major steps and their order are expected to remain consistent.
  • Clear variation points: Only a bounded set of steps differs between variants.
  • Explicit obligations: You can identify which operations every subclass must implement and which have safe defaults.
  • Acceptable coupling: Subclasses can depend on the base-class lifecycle and its protected API.
  • Compile-time variants: The set of variants is represented by types rather than selected dynamically for each call.

Inheritance is a weaker fit when the sequence changes frequently, when clients must switch behavior at runtime, or when implementations need to combine several independent policies. In those cases, composition with strategy objects or injected functions usually keeps variation more independent from the workflow.

Situation Template Method assessment
One lifecycle, several fixed implementations Strong fit
Most steps are shared; one or two are mandatory differences Strong fit
Optional behavior is common but has a safe default Good fit with hooks
Behavior must be swapped at runtime per request Consider Strategy or composition
Subclasses need to reorder core steps Usually a sign that the base sequence is not truly stable
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common mistakes and how to avoid them

Making every step abstract

If all operations are abstract, the base class contributes little beyond a method call list. Implement genuinely invariant work in the base class and reserve abstract methods for real variation.

Allowing subclasses to bypass the lifecycle

An overridable template method lets a subclass skip validation or cleanup. Make it final, or clearly document which parts may be replaced.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Using hooks for mandatory behavior

An empty default can hide a missing essential action. Make required work abstract so the compiler enforces it.

Exposing too much protected state

Protected fields and numerous hooks create a fragile subclass contract. Prefer small methods with explicit arguments and return values.

Changing the sequence without checking callers

Reordering steps can alter security checks, transactions, resource handling, and output. Treat the template method as an API contract and test the ordering directly.

Checklist for implementing the pattern

  1. Write the algorithm as an ordered list of steps.
  2. Mark each step invariant, required variation, or optional variation.
  3. Implement invariant steps in the base class.
  4. Declare required variations abstract.
  5. Give optional variations safe, documented hook defaults.
  6. Decide whether the template method should be final.
  7. Keep the subclass API small and test both the order and the extension-point behavior.

The Bottom Line

Use Template Method when one process has a stable sequence and a controlled set of subclass variations. Put that sequence in the base class, distinguish abstract required operations from default hooks, and protect the workflow with a final template method when order is part of the contract.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.