Home Design Patterns Template Method

Template Method

Behavioral Gang of Four

“Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.”

Gamma, Helm, Johnson & Vlissides, p. 325

Purpose

Template Method writes down the order of a process once, in a base class, and leaves holes where the parts that differ go. Subclasses fill the holes. They do not get to change the order, and that restriction is the whole value of the pattern rather than a limitation of it.

It is the pattern that defines what a framework is. Ordinary library code is called by you; a template method calls you, at the point it has decided, with the guarantees it has decided. Everything anyone has ever written about inversion of control is a description of this one mechanism.

Design

An abstract class holds one concrete method, the template method, which calls a sequence of other operations. Some of those are abstract and must be supplied by a subclass. Some are hooks: they have a default implementation, usually a harmless one, and a subclass may override them but does not have to. The template method itself should not be overridable, and in a language that lets you say so you should say so.

UML class diagram: AbstractClass defines templateMethod plus abstract primitive operations and a hook, with two concrete subclasses implementing the primitives.
One hierarchy, no aggregation anywhere. This is the only pattern in the behavioral chapter whose entire mechanism is inheritance, which is exactly what makes it the useful contrast with Strategy.

Keeping the abstract steps to a minimum matters more than it looks. Every abstract operation is something a subclass author must implement correctly for the algorithm to work, and each one is a chance to get it wrong. The best template methods have one or two abstract steps and several hooks, so that the common case requires almost nothing and the unusual case is still possible.

Example

Every job an Autobot Robotics arm runs has the same shape around it: confirm the fitted tool matches the job, take a reading from the workpiece fixture, run the job-specific motion, park the arm, and write a cycle record. The drilling program, the pick-and-place program and the workspace cleaner all did those five things, and all three had arrived at that sequence by copying whichever one existed first.

The copies drifted, as copies do. A correction to the parking step, which required parking the arm before dropping the motion interlock rather than after, was applied to two of the three. The third left an arm energized over a fixture overnight. Nothing was damaged, but the review that followed made the point better than any argument about code duplication: the process was a rule about the machine, and it was stored in three places with no mechanism that kept them equal.

The sequence now lives in ArmJob.run(), which is concrete and sealed. Subclasses supply execute() and may override the recordCycle() hook. The cost showed up quickly and is worth being honest about: a customer with a fixture that could not be read needed to skip the fixture step, and because run() is sealed they could not simply override it. The team added a hook. That is the pattern working correctly, and it is also how a base class turns into a policy authority with a growing list of exemptions carved into it.

abstract class ArmJob
{
    // The template method. Deliberately not virtual: the order of these
    // five steps is the thing the class exists to guarantee.
    public void run()
    {
        verifyTool();
        readFixture();
        execute();                  // the one step a subclass must supply
        park();
        if (shouldRecordCycle())    // hook, defaults to yes
            writeCycleRecord();
    }

    protected abstract void execute();

    protected virtual bool shouldRecordCycle() { return true; }

    private void verifyTool()       { /* compare fitted tool to the job */ }
    private void readFixture()      { /* take the fixture reading */ }
    private void park()             { /* park, then drop the interlock */ }
    private void writeCycleRecord() { /* append to the cycle log */ }
}

class DrillJob : ArmJob
{
    protected override void execute()
    {
        // drive the drill through the hole pattern
    }
}

class CleanJob : ArmJob
{
    protected override void execute() { /* sweep the workspace */ }

    // A cleaning pass is not a production cycle, so it is not recorded.
    protected override bool shouldRecordCycle() { return false; }
}
run() is the pattern and its lack of virtual is not an oversight. execute() is the hole, shouldRecordCycle() is the hook, and the difference between them is that one must be answered and the other already has an answer.

Seen in the Wild

Java's AbstractList is a clean example: it implements iterator(), indexOf(), equals() and the rest in terms of the two operations it leaves abstract, get(int) and size(), so a new list type supplies two methods and inherits a working collection. HttpServlet.service() is the same idea used as a dispatcher: it inspects the request method and calls doGet, doPost or one of their siblings, all of which are hooks with default implementations that return an error. JUnit 3's TestCase.runBare() ran setUp(), then the test, then tearDown() in a finally, which is a template method whose entire purpose is the guarantee that the last step happens.

Comparisons

  • Strategy is the same problem answered with composition, and the two together are the clearest demonstration of "favor composition over inheritance" in the catalog. Template Method is cheaper: no extra object, no extra interface, and the varying step can see the base class's protected members for free. Strategy is more capable: the algorithm can change on a live object, two dimensions can vary at once, and the algorithm can be reused by something outside the hierarchy. If you only ever need one axis of variation and the choice is fixed when the object is built, Template Method is the smaller correct answer, and the maxim is not an instruction to use Strategy anyway.
  • Factory Method is very often one of the steps. A template method that needs to create something without knowing its class calls an abstract creation operation, which is precisely a factory method. The Gang of Four say factory methods are usually called by template methods, and in practice that is where most of them turn up.
  • Builder has a Director whose construct() is a template method built with composition instead of inheritance: it holds the builder rather than being subclassed by it.
  • Decorator also wraps behavior around a call, but from the outside and at run time. A decorator adds a layer without knowing what it wraps; a template method knows the whole sequence and only defers the contents.
  • Extensibility is what this pattern looks like once it is applied to a whole system rather than to one algorithm.

Why It Exists

My take

Template Method exists because processes get copied. Not algorithms in the textbook sense, but sequences: connect, authenticate, transfer, verify, close. Open, lock, write, unlock, flush. Somebody writes one, somebody else needs almost the same one and copies it, and now there are two definitions of a rule that was only ever supposed to have one. What makes this expensive is not the duplicated lines, it is that the divergence is silent. Nothing fails when copy three misses a correction. It fails months later, once, in a way that does not point back at the copy.

The reason it is the most-used pattern in the book despite being the least discussed is that it is what every framework is made of. When you subclass a test case, a servlet, a form, a view controller, or a driver base class, you are filling holes in somebody's template method. Most programmers use this pattern daily and would not name it, and I think that is a sign it landed on something fundamental rather than that it is too trivial to mention.

The position I hold, and it is slightly unfashionable, is that this is the case where inheritance is genuinely the right tool and the standard advice overshoots. "Favor composition over inheritance" is good advice about reusing implementations. It is weaker advice about defining a protocol, which is what a template method does. If the sequence is a rule that must not vary, a sealed method in a base class states that in the type system and a strategy object does not. The place the advice does apply is the moment you want two skeletons, or want to pick one at run time. At that point inheritance has run out and you should convert to Strategy rather than add a third level to the hierarchy.

Criticisms

It is the fragile base class problem in its purest form. Subclasses depend not only on what the base class declares but on when it calls them and what state it is in at the time, and none of that is expressed in a signature. Reordering two steps in the template method is a source-compatible change that can break every subclass in the system, including subclasses in code you do not own. There is no way to write down the contract that "readFixture has already run by the time execute is called", so it lives in a comment, and comments do not fail the build.

There is a specific and nasty version of that in most object-oriented languages. If the template method is called from the base class constructor, the overridden steps run against a subclass that has not finished initializing, so its fields are still default values. C# and Java both dispatch virtually during construction and both will happily hand you a half-built object; C++ makes the opposite choice and silently calls the base version instead, which is a different surprise. Every code analyzer flags this and it still ships, because the base class author and the subclass author are usually different people at different times.

Finally, it spends your inheritance. In a single-inheritance language a class can participate in exactly one template method, so a job that wants the arm process skeleton and the reporting skeleton cannot have both. Hooks accumulate for the same reason: every legitimate exception to the sequence has to be carved into the base class as another overridable, and after enough of them the skeleton is no longer a skeleton, it is a configuration surface with a call order.