$ emrebener
home topics gof design patterns (behavioural) template method pattern

Template Method Pattern

author: emre bener read time: 6 min about: template method pattern, java

1. What the template method pattern is

The template method pattern puts the skeleton of an algorithm in a base class and lets subclasses fill in the steps that vary. The base class fixes the order of operations once; subclasses change what each step does, never when it runs.

Concretely, the base class has one method (the template method) that calls a sequence of smaller steps. Some of those steps are implemented in the base class because they never change. Others are left abstract, and each subclass overrides them. Every subclass ends up running the exact same algorithm structure, with its own behaviour plugged into the gaps.

The Gang of Four filed it under behavioural patterns because the load-bearing decision is about responsibility: the base class owns the control flow, and subclasses own the details. Neither side can take the other’s job. A subclass cannot reorder the steps, and the base class cannot know what the steps actually do. That split is the pattern.

2. The template method, primitive operations, and hooks

The pattern has three parts: the template method that holds the algorithm’s skeleton, the primitive operations a subclass must supply, and the hooks it may optionally override.

The template method is the method that holds the algorithm’s skeleton. It calls the steps in a fixed order and is the only thing a caller invokes. It should be final. If a subclass could override it, the subclass could rewrite the algorithm, and the pattern’s one guarantee (every subclass runs the same structure) evaporates.

Primitive operations are the abstract steps. The base class declares them but does not implement them; each subclass must. These are the mandatory gaps in the algorithm, the parts that actually differ between subclasses.

Hooks are optional steps. The base class gives them a default implementation (often empty, or a sensible default) so a subclass can override them but doesn’t have to. A hook lets a subclass tune the algorithm at a specific point without being forced to. The base class also decides whether a hook even gets called, which is how a hook can switch an optional step on or off.

This arrangement inverts the usual direction of control. You don’t write a subclass that calls into a library; you write a subclass that the base class calls back into. The base class runs the show and reaches down into your code for the variable parts. The Gang of Four call this the Hollywood principle: “don’t call us, we’ll call you.” Same inversion as framework callbacks and dependency injection, here through plain inheritance.

3. Writing one in Java

The cleanest example is the beverage recipe from Head First Design Patterns: making coffee and making tea are almost the same procedure. Both boil water, brew something, pour it into a cup, and maybe add condiments. Only the brew step and the condiments step actually differ.

That “almost the same” is the signal to reach for the pattern. The base class captures the shared procedure as a final template method and leaves the two variable steps abstract:

public abstract class CaffeineBeverage {

    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) {
            addCondiments();
        }
    }

    protected abstract void brew();

    protected abstract void addCondiments();

    private void boilWater() {
        System.out.println("Boiling water");
    }

    private void pourInCup() {
        System.out.println("Pouring into cup");
    }

    protected boolean customerWantsCondiments() {
        return true;
    }
}

prepareRecipe() is the template method. It is final, so no subclass can change the order of the four steps. boilWater() and pourInCup() are private because they never vary, and there is no reason to expose them. brew() and addCondiments() are the primitive operations: abstract, so every subclass is forced to supply them. customerWantsCondiments() is a hook: it has a default (true), the template method consults it to decide whether the condiments step runs at all, and a subclass can override it or ignore it.

A subclass is then just the variable parts, with nothing about ordering or control flow:

public class Coffee extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println("Dripping coffee through filter");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

Coffee ignores the hook, so it accepts the default and always adds condiments. Tea overrides the hook to opt out:

public class Tea extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println("Steeping the tea");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding lemon");
    }

    @Override
    protected boolean customerWantsCondiments() {
        // a purist's tea: skip the lemon
        return false;
    }
}

prepareRecipe()«final template method»boilWater() — base classbrew() — abstract primitivepourInCup() — base classcustomerWantsCondiments() — hookaddCondiments() — abstractprimitiveCaffeineBeverage«abstract base class»CoffeeTeaextendsextendsCoffee and Tea fill in theabstract steps (blue). Tea alsooverrides the hook (orange);the base class owns the rest (gray).prepareRecipe()«final template method»boilWater() — base classbrew() — abstract primitivepourInCup() — base classcustomerWantsCondiments() — hookaddCondiments() — abstractprimitiveCaffeineBeverage«abstract base class»CoffeeTeaextendsextendsCoffee and Tea fill in theabstract steps (blue). Tea alsooverrides the hook (orange);the base class owns the rest (gray).

Calling new Coffee().prepareRecipe() and new Tea().prepareRecipe() runs the identical four-step skeleton; only the brew line, the condiments line, and whether the condiments step happens at all change. Adding hot chocolate later means one more subclass and zero edits to CaffeineBeverage, since the algorithm’s structure is already locked and shared.

4. Where the JDK uses it

The template method pattern is dense in the standard library, usually wherever a class name starts with Abstract or a framework dispatches to methods you override.

HttpServlet.service() is the textbook case. It is the template method: it inspects the HTTP request method and dispatches to doGet() doPost(), doPut(), and the rest. Those are the primitive operations, and writing a servlet means overriding the one or two you care about while service() owns the dispatch logic.

AbstractList defines iterator(), indexOf(), equals(), and hashCode() as template methods written entirely in terms of two abstract primitives, get(int) and size(). Implement those two and you inherit a working List. AbstractMap does the same trick on top of entrySet().

InputStream.read(byte[]) is a quieter example: it loops, calling the single-byte abstract read() once per element. A subclass implements one-byte-at-a-time reading and gets bulk reads for free.

Same job in every case as in §3: the library owns the control flow and an algorithm’s shape, and your subclass supplies only the steps that differ.

5. Template Method vs Strategy, and when to skip it

Template Method and Strategy both let part of an algorithm vary, but they pick opposite tools to do it, and the choice matters.

Template Method varies behaviour through inheritance. The variable steps are methods on a subclass, bound at compile time. One object is one beverage; you cannot turn a Coffee into a Tea at runtime, and the shared skeleton lives in a base class the subclass cannot escape.

Strategy varies behaviour through composition. The variable behaviour is a separate object held in a field, so it can be swapped at runtime, shared between hosts, and tested in isolation. The cost is more moving parts: an interface, the implementations, and the wiring to inject one.

The rule of thumb: reach for Template Method when the algorithm’s skeleton is genuinely fixed and the variation is small, closed, and known at compile time. Reach for Strategy when the variable behaviour needs to change at runtime or be reused outside this one hierarchy.

A few cases where Template Method is the wrong call even when an algorithm has variable steps:

  • The variation needs to change at runtime. Inheritance binds the steps when the object is constructed. If a single object must behave differently over its lifetime, that’s Strategy’s job, not this one.
  • The hierarchy is getting deep. Template Method costs one subclass per variant. Two independent axes of variation (say, three brew methods times three condiment sets) means nine subclasses. Composition handles that combinatorial blow-up; inheritance multiplies it.
  • The fragile base class problem. Every subclass is welded to the base class’s step sequence and method signatures. Change the template method’s shape and you can silently break subclasses you’ve never seen, especially across a library boundary. The tighter the coupling, the more a change to the base class ripples outward.

When the skeleton is stable and the variants are few, those costs stay small and the pattern is hard to beat: it removes duplication with almost no ceremony. When they aren’t, the same inheritance that made the pattern cheap is what makes it hurt.

Template Method — inheritanceBeverage «abstract»prepareRecipe() «final»Coffeeoverrides brew()Teaoverrides brew()extendsextendsVariant is fixed when theobject is constructed.Strategy — compositionBeverageMaker «context»holds a BrewStrategy fieldBrewStrategy«interface»CoffeeBrewTeaBrewimplementsimplementsStrategy can be swappedat runtime.Template Method — inheritanceBeverage «abstract»prepareRecipe() «final»Coffeeoverrides brew()Teaoverrides brew()extendsextendsVariant is fixed when theobject is constructed.Strategy — compositionBeverageMaker «context»holds a BrewStrategy fieldBrewStrategy«interface»CoffeeBrewTeaBrewimplementsimplementsStrategy can be swappedat runtime.