Template Method - Programming in the Large with Design Patterns (2012)

Programming in the Large with Design Patterns (2012)

Chapter 11. Template Method

Introduction

Joe Johnson, lead foreman at Peak Roofing, was asked by his boss to prepare written procedures for performing their company’s signature service: roof replacement. Joe quickly concluded that it would be impractical to create just one set of procedures, as different customers had different priorities. For example, some customers were price sensitive while others were more concerned with the quality of the work.

To get started, Joe decided to write two separate procedure manuals, one for replacing a roof at minimal cost and another for replacing a roof when quality was the first priority.

Here is the result:

Cheap Roof Replacement

Step 1: Prepare jobsite. Secure safe access to roof.

Step 2: Prepare surface for new roof. If there is only one layer of existing roof shingles, leave them on. Otherwise, remove all existing layers.

Step 3: Install new roof.

Step 4: Clean jobsite. If there is any roofing debris around the perimeter of the house, till it into the soil or sweep it under the porch when the homeowner isn’t looking.

Quality Roof Replacement

Step 1: Prepare jobsite. Secure safe access to roof. Cover plants around the perimeter of the house.

Step 2: Prepare surface for new roof. Remove all existing layers of roofing shingles. Add membrane to prevent ice dams.

Step 3: Install new roof.

Step 4: Clean jobsite. Remove plant covers and any remaining debris around the perimeter of the house.

One concern with having two manuals is the overlap between the two procedures. They both share the same steps, and some steps include the same activities. Keeping both documents consistent and up-to-date is going to be a challenge.

As you might expect, this is a problem for which the Template Method design pattern is ideally suited. The Template Method design pattern shows how to represent an algorithm with customizable steps in a way that reduced redundancy. The problem presented here is analogous. The algorithm or basic steps for replacing a roof is the same whether the goal is to minimize cost or maximize quality. Only the detailed activities within the steps are different.

Figure 74 shows how the problem presented here can be solved with the Template Method design pattern. The invariant parts of the procedure are declared in an abstract base class. The variant parts are declared in subclasses. The sequence of steps for replacing a roof are defined in the template method replaceRoof(). Subclasses—one for each type of roof replacement—override methods called from replaceRoof() to implement variations on the basic procedure.

Figure 74 Conceptual example of Template Method design pattern

Client code for the above design would look something like:

QualityRoofingService service =

new QualityRoofingService();

service.replaceRoof();

Intent

One indication there is an opportunity to improve existing code with the Template Method design pattern is having two or more procedures or code fragments with the same basic structure but different detailed steps. For example, imagine finding the following two procedures in a program:

void growTomatoes () {

digHole();

plantSeeds();

if (isWarmClimate())

mulch();

while (! frost())

water();

}

void growPumpkins () {

digHole();

plantSeeds();

while (! frost()) {

water();

prune();

}

}

After reading through both procedures it is clear something is being duplicated, but exactly what might not be immediately obvious. The procedures are similar but not duplicates of each other. You simply can’t replace one with the other because the details of each are different.

What is being duplicated here is the structure of the algorithm for growing flowering plants. If in the future you wanted to add say a harvest() function, you would have to update the growing procedures for all flowering plants, even if the harvest() function was the same for all plants.

Duplicate code complicates maintenance and increases the risk of introducing bugs when making changes. It is important to be able to recognize duplication and understand how to eliminate it in whatever form it may take. The Template Method design pattern shows how to eliminate repetition in algorithm structure.

With the Template Method design pattern the structure of an algorithm is represented once with variations on the algorithm implemented by subclasses. The skeleton of the algorithm is declared in a template method in terms of overridable operations. Subclasses are allowed to extend or replace some or all of these operations.

Solution

With the Template Method design pattern, an algorithm is broken down into primitive operations. The skeleton of the algorithm is defined in a template method that resides in an abstract base class. Concrete subclasses implement variations on the algorithm by overriding specific operations.

Figure 75 Template Method design pattern

There are three different types of operations called from template methods:

Sample Code

The example in this section shows how to restructure growTomatoes() and growPumpkins() to use the Template Method design pattern.

Recall the similarity between the two procedures:

Analyzing the individual steps we can extract the general algorithm for growing a vegetable:

void growVegetable() {

digHole();

plantSeeds();

mulch();

while (!frost()) {

water();

prune();

}

}

Actions such as mulch() and prune(), which are applicable to some but not all vegetables, are included in the general algorithm. Subclasses will have the chance to ignore or override the behavior of these operations.

The next step is to decide how to declare each method called from the template method. Methods may be declared as: (1) abstract, (2) concrete with default implementation, (3) concrete with empty implementation, and (4) non-polymorphic.

The following guidelines were used in the design of this example:

1. Methods expected to have unique implementation in most subclasses were declared abstract.

2. Methods expected to share the same implementation in many subclasses were declared concrete and given default implementation.

3. Methods needed for only a small fraction of subclasses were declared concrete and given empty implementation.

4. Methods representing invariant steps in the algorithm were declared non-polymorphic. If the method might be useful to a subclass, it was declared protected. Otherwise, it was declared private.

Here is the complete program:

abstract class Vegetable {

// helper class used to consolidate weather

// information

private Weather weather = new Weather();

// Template method

public void grow() {

digHole();

plantSeeds();

mulch();

while (! weather.frost()) {

water();

prune();

}

}

protected void digHole() {

System.out.println("Dig hole");

}

protected abstract void plantSeeds();

protected void mulch() { }

protected void water() {

System.out.println("Water plant");

}

protected void prune() { }

// Delegate method available to subclasses

protected final boolean isWarmClimate() {

return weather.isWarmClimate();

}

}

class Tomato extends Vegetable {

protected void plantSeeds(){

System.out.println("Plant tomato seeds");

}

protected void mulch() {

if(isWarmClimate())

System.out.println("Mulch around plant");

}

}

class Pumpkin extends Vegetable {

protected void plantSeeds(){

System.out.println("Plant pumpkin seeds");

}

protected void prune() {

System.out.println("Selectively prune new side shoots");

}

}

// helper class

class Weather {

private boolean toggle = true;

public boolean frost() {

toggle = !toggle;

return toggle;

}

public boolean isWarmClimate() {

return true;

}

}

public class Runner {

public static void main(String[] args) {

Tomato t = new Tomato();

t.grow();

Pumpkin p = new Pumpkin();

p.grow();

}

}

Output:

Dig hole

Plant tomato seeds

Mulch around plant

Water plant

Dig hole

Plant pumpkin seeds

Water plant

Selectively prune new side shoots

Discussion

A template method is often described as encapsulating an algorithm. Don’t be misled by the term algorithm though. There are many practical applications of the Template Method design pattern where the “algorithm” is just a few lines of code.

One particular application of the Template Method design pattern that doesn’t depend on an algorithm in the traditional sense is when the pattern is used as a remedy for the Call Super code smell.

A symptom of the Call Super code smell is a class with a polymorphic method that requires an overriding method to make an explicit call to the overridden method. For example, in the class definition below the comments associated with the method harvest() stipulate that overriding methods must call the base class implementation of harvest().

abstract class Vegetable {

private DateTime lastHarvest;

// Overriding methods must call back to this

// method at the beginning of their implementation.

public void harvest() {

if (DateTime.Today < lastHarvest.AddDays(5))

log(“Early Harvest”);

lastHarvest = DateTime.Today;

}

}

The correct functioning of the class Vegetable depends on the ability to detect early harvests. (For the purposes of this example, an early harvest is one that occurs within 5 days of the previous harvest.) A proper subclass implementation would look something like:

class Carrots extends Vegetable {

public void harvest() {

super.harvest();

. . .

}

}

Any design that relies on programmers remembering to do something should be questioned. In this case there is a better solution and that is to make harvest() a template method. To do this you would add a call in harvest() to a new method (doHarvest() in the example below) and make this new method the extension point for subclasses.

Here is the refactored solution:

abstract class Vegetable {

private DateTime lastHarvest;

// Template method

// Subclasses wanting to extend the behavior of

// of harvest() should override doHarvest().

public void harvest() {

if (DateTime.Today < lastHarvest.AddDays(5))

log(“Early Harvest”);

lastHarvest = DateTime.Today;

doHarvest();

}

// New method for subclasses to override

protected void doHarvest() { }

}

With this new design, subclasses wanting to extend the behavior of harvest() would override doHarvest() rather than harvest().

class Carrots extends Vegetable {

public void doHarvest() {

. . .

}

}

The general solution applied here is more broadly applicable (i.e not just for remedying the Call Super code smell). The general solution is applicable anytime you want to exercise more control over class extensibility.

With simple inheritance a client-facing operation is made polymorphic. Subclasses can then extend or replace the behavior of the operation. The interface for clients is the same as the interface for subclasses (see Figure 76). Because subclasses can replace the behavior of the operation, the base class gives up complete control over what clients get when they call the operation through a reference to the base class (A in Figure 76).

Figure 76 With simple inheritance, the interface for clients is same as interface for subclasses

Sometimes you need more control over how a class is extended. In situations like this you can use the Template Method design pattern to separate the interface for clients from the interface for subclasses (see Figure 77). With this arrangement you can guarantee certain minimal behavior to clients but still allow for extension by subclasses.

Figure 77 A template method allows the interface for clients to be separate from the interface for subclasses

Something else to consider when implementing the Template Method design pattern is whether to make the template method polymorphic or non-polymorphic. In the code fragment in Figure 77 the template method f() is declared final which means subclasses are not allowed to override it. This limits the freedom to change the behavior of the class in subclasses but it also guarantees the class won’t be used by subclasses in ways that weren’t intended.

Related Patterns

Template Method and Strategy solve similar problems. They both solve the problem of how to represent variations on algorithms. The main difference between the two patterns is Template Method uses inheritance to vary parts of an algorithm and Strategy uses delegation to vary the entire algorithm.

A Template Method defines an algorithm with replaceable steps. One of the steps may be a Factory Method (i.e. a method that returns an instance of an object).

Bibliography

Alexander, Christopher. The Timeless Way of Building. Oxford: Oxford University Press, 1979.

Alexander, Christopher, Sara Ishikawa, and Murray Silverstein. A Pattern Language: Towns, Buildings, Construction. Oxford: Oxford University Press, 1977.

Beck, Kent, and Ward Cunningham. "Using Pattern Languages for Object-Oriented Programs." Tektronix Technical Report No. CR-87-43, 1987.

Buschmann, Frank, Regine Meunier, Hans Rohnert, Peter Sommerlad, and Michael Stal. Pattern-Oriented Software Architecture: A System of Patterns. John Wiley & Sons, 1996.

Coplien, James O. Advanced C++ programming styles and idioms. Reading MA: Addison-Wesley, 1992.

Fowler, Chad. Rails Recipes: Rails 3 Edition. Pragmatic Bookshelf, 2012.

Fowler, Martin. Analysis Patterns: Reusable Object Models. Addison-Wesley Object Technology Series, 1997.

Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional Computing Series, 1995.

Margolis, Michael. Arduino Cookbook. O'Reilly Media, 2011.

Martelli, Alex, Anna Ravenscroft, and David Ascher. Python Cookbook. O’Reilly, 2005.

Shaw, Mary, and David Garlan. Software Architecture: Perspectives on an Emerging Discipline. Prentice Hall, 1996.

Smith, Reid. "Panel on Design Methodology." In OOPSLA '87 Addendum to the proceedings on Object-oriented programming systems, languages and applications (Addendum). ACM Press, 1987. 91-95.