Low-level designCore8 min

Template Method

Write the steps of a process once, in a base class, and leave the steps that differ to subclasses. The order is fixed in one place, and a new variation fills in only what is its own.

Watch it evolve1 / 9
The problem

Two kinds of print job

CardJob and PosterJob each run the routine: check, lay out, print, finish.

80
Start
Stage 1
Stage 2
Final

1 · The situation

Two kinds of print job, one routine

A print shop runs every job the same way: check the customer's file, lay the pages out on press sheets, print, and finish. Business cards are imposed 21 to a sheet, then cut and boxed. Posters are tiled across 4 sheets each and laminated, and the customer approves a proof first, since a poster reprint is expensive.

The first version has a class per kind of job, CardJob and PosterJob, each with its own run() that walks the routine from top to bottom.

The first versionJava
class CardJob {
    private final String file;
    private final int copies;

    CardJob(String file, int copies) {
        this.file = file;
        this.copies = copies;
    }

    void run() {
        System.out.println("Job: " + copies + " business cards");
        System.out.println("  check " + file);
        int sheets = (copies + 20) / 21;
        System.out.println("  impose 21 cards per sheet");
        System.out.println("  print " + sheets + " sheets");
        System.out.println("  cut and box");
    }
}

class PosterJob {
    private final String file;
    private final int copies;

    PosterJob(String file, int copies) {
        this.file = file;
        this.copies = copies;
    }

    void run() {
        System.out.println("Job: " + copies + " posters");
        System.out.println("  check " + file);
        System.out.println("  send a proof to the customer");
        int sheets = copies * 4;
        System.out.println("  tile each poster on 4 sheets");
        System.out.println("  print " + sheets + " sheets");
        System.out.println("  laminate");
    }
}

public class Main {
    public static void main(String[] args) {
        new CardJob("cards-ruiz.pdf", 500).run();
        new PosterJob("fair-poster.pdf", 3).run();
    }
}
CardJob− file: String− copies: int~ run()PosterJob− file: String− copies: int~ run()

The class diagram of this version, drawn from its code: two unrelated classes with the same shape.

One order of cards and one of posters:

Job: 500 business cards
  check cards-ruiz.pdf
  impose 21 cards per sheet
  print 24 sheets
  cut and box
Job: 3 posters
  check fair-poster.pdf
  send a proof to the customer
  tile each poster on 4 sheets
  print 12 sheets
  laminate

2 · The change

The routine lives in every job class

Two changes arrive at once. The shop adds booklets, and the owner wants one change to the routine for every job: a check that the file's fonts are embedded. Look at where the routine is today:

The shared steps, written in each classJava
class CardJob {
    private final String file;
    private final int copies;

    CardJob(String file, int copies) {
        this.file = file;
        this.copies = copies;
    }

    void run() {
        System.out.println("Job: " + copies + " business cards");
        System.out.println("  check " + file);
        int sheets = (copies + 20) / 21;
        System.out.println("  impose 21 cards per sheet");
        System.out.println("  print " + sheets + " sheets");
        System.out.println("  cut and box");
    }
}

class PosterJob {
    private final String file;
    private final int copies;

    PosterJob(String file, int copies) {
        this.file = file;
        this.copies = copies;
    }

    void run() {
        System.out.println("Job: " + copies + " posters");
        System.out.println("  check " + file);
        System.out.println("  send a proof to the customer");
        int sheets = copies * 4;
        System.out.println("  tile each poster on 4 sheets");
        System.out.println("  print " + sheets + " sheets");
        System.out.println("  laminate");
    }
}

Half of each run() is the same in both classes, and it's the half that defines how the shop works:

Step

Same for every job?

Today written in

Announce the job

Yes

Every class

Check the file

Yes

Every class

Send a proof

Only for some jobs

The classes that need it

Lay out the sheets

No

Each class

Print

Yes

Every class

Finish

No

Each class

A booklet class would be a third copy of the routine. The font check is a change to every copy, in the same place in each, and the copies can drift: a class that checks fonts after printing compiles and runs just as well.

3 · The refactor

Write the routine once, and leave gaps

Start from the first version. Move the routine into a base class, and turn each step that differs into a method the subclasses supply:

PrintJob.run() holds the fixed order: announce, check, proof if needed, lay out, print, finish. It's final, so a subclass can't reorder it. The steps it calls, product(), layout(), sheets() and finishing(), are abstract.

Java
abstract class PrintJob {
    protected final String file;
    protected final int copies;

    PrintJob(String file, int copies) {
        this.file = file;
        this.copies = copies;
    }

    final void run() {
        System.out.println("Job: " + copies + " " + product());
        System.out.println("  check " + file);
        if (needsProof()) System.out.println("  send a proof to the customer");
        System.out.println("  " + layout());
        System.out.println("  print " + sheets() + " sheets");
        System.out.println("  " + finishing());
    }

    protected boolean needsProof() {
        return false;
    }

    protected abstract String product();

    protected abstract String layout();

    protected abstract int sheets();

    protected abstract String finishing();
}
«abstract»PrintJob# file: String# copies: int~ run()# needsProof(): boolean# product(): String# layout(): String# sheets(): int# finishing(): StringCardJob# product(): String# layout(): String# sheets(): int# finishing(): StringPosterJob# product(): String# needsProof(): boolean# layout(): String# sheets(): int# finishing(): String

After the second step the program prints exactly what it printed before. After the third, booklets run through the same routine:

Job: 500 business cards
  check cards-ruiz.pdf
  impose 21 cards per sheet
  print 24 sheets
  cut and box
Job: 3 posters
  check fair-poster.pdf
  send a proof to the customer
  tile each poster on 4 sheets
  print 12 sheets
  laminate
Job: 50 booklets
  check garden-zine.pdf
  send a proof to the customer
  fold 16 pages onto 4 sheets
  print 200 sheets
  fold and staple
Routine in each job class
3
Routine in PrintJob
1

Copies of the routine with three kinds of job. The font check now goes into one method, PrintJob.run().

4 · The pattern

Template Method: a fixed skeleton with steps to fill

Template Method defines the skeleton of a procedure in a base class method, the template method, and leaves some of its steps to subclasses. The base class decides the order and what happens between steps; subclasses decide what each step does. Steps that subclasses must supply are abstract; steps they may change are hooks, with a default.

«abstract»PrintJob# file: String# copies: int~ run()# needsProof(): boolean# product(): String# layout(): String# sheets(): int# finishing(): StringCardJob# product(): String# layout(): String# sheets(): int# finishing(): StringPosterJob# product(): String# needsProof(): boolean# layout(): String# sheets(): int# finishing(): StringBookletJob# product(): String# needsProof(): boolean# layout(): String# sheets(): int# finishing(): String

The roles in the print shop's code: PrintJob is the abstract class with the template method, and each job a concrete class.

Role

In the print shop

Its job

Abstract class

PrintJob

Holds the template method and declares the steps

Template method

PrintJob.run()

Fixes the order of the steps; final

Abstract steps

product(), layout(), sheets(), finishing()

Must be supplied by every subclass

Hook

needsProof()

Has a default; subclasses override it only when they need to

Concrete class

CardJob, PosterJob, BookletJob

Supplies the steps for one kind of job

The inherited run() calls down into the subclass for each step it leaves open.

The base class calls the subclass, not the other way around: subclasses never call run()'s steps themselves or decide the order. That reversal is what keeps the routine in one place.

5 · What it costs

Inheritance, with its ties

  • It's inheritance. Subclasses depend on the base class's protected methods and on when the template calls them, the fragility described in Inheritance. A change to the template can break every subclass.
  • One skeleton per class. A job can't take its layout from one variation and its finishing from another; each combination is a subclass.
  • The order is fixed for everyone. That's the point, until one job genuinely needs a different order; then the template grows a hook, or the job doesn't fit.

Situation

Template Method or something else?

Several classes run the same procedure with different steps

Template Method

Steps should be mixed and matched at run time

Strategy: hold each varying step as an object

Only one step varies

A Strategy, or a function passed in, is lighter

The procedure itself differs between variations

Neither: they are different procedures

6 · Getting it right

Lock the order, open only the steps

  • Make the template method impossible to override. In Java mark it final. Otherwise a subclass can replace the whole routine, and the shop's checks with it.
  • Keep the steps out of reach of callers. Steps are protected: subclasses supply them, but only the template calls them.
  • Few abstract steps, sensible hooks. Every abstract step is work for every subclass. Give optional steps a default, as needsProof() returns false.
  • Keep the steps pure where you can. Here the steps return text and numbers and run() prints, so each step is easy to test and the output format lives in one place.

Language

Fixing the order

Java

A final template method calling protected abstract steps

C++

A public non-virtual template calling protected virtual steps: the non-virtual interface idiom

JavaScript

No final or protected; the base class documents which methods subclasses supply

Python

abc.abstractmethod for the steps, and typing.final on the template, which type checkers enforce

7 · Look-alikes

Fixed steps, varied by inheritance or by objects

Template Method shares its goal, varying part of an algorithm, with other patterns. What differs is how the varying part is supplied:

Pattern

The varying part

Chosen

Template Method

Steps of a fixed procedure, supplied by subclasses

When the class is written

Strategy

A whole job, supplied by an object the context holds

At run time, and can be swapped

Factory Method

Which object to create, supplied by a subclass

When the class is written

Hooks and callbacks

A function the framework calls at set points

At run time, by registering it

A factory method is often one step of a template method: the template does the common work and calls createSomething() where a subclass decides what to make. Template Method and Strategy are the two classic ways to vary part of an algorithm: by inheritance or by composition.

8 · In practice

Templates in the standard library

Java's AbstractList is a template: to make an unmodifiable list, a class extends it and supplies get(int) and size(), and every other List method, iteration, searching, comparison, is built on those two. To make a modifiable list it also overrides set(), and for a resizable one add() and remove().

The practice checks recognize a Template Method when an abstract class has a concrete method that calls at least two of its own steps, one of them abstract, and subclasses supply them. A template that is final counts in its favor, and a subclass that overrides the template itself is flagged.

Check yourself

4 questions

1. The shop adds a font check before printing, for every job. Where does it go?
2. Why is run() final?
3. A job needs a proof only when the order is over $500. How should it do that?
4. The shop wants rush finishing to be combinable with any job type. Is Template Method a good fit for that?

Takeaways

Remember this

  • Template Method writes a procedure once in a base class and leaves the varying steps to subclasses.
  • The base class calls the subclass's steps, never the other way around.
  • Abstract steps must be supplied; hooks have defaults subclasses may override.
  • Make the template method final so the order can't be changed.
  • It varies behavior by inheritance; Strategy does it by composition.