The Template Method Pattern

The Template Method Pattern

Introduction

The template method pattern is one of the classical 23 patterns described by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides in their high-impact book Design Patterns: Elements of Reusable Object-Oriented Software.

The template method pattern is one of the simplest, yet very efficient patterns. A template method describes an algorithm consisting of a series of sub-steps. While the order of these steps is fixed, their behavior might vary. Subclasses are responsible for implementing the appropriate behavior.

Gamma et al. also known as the Gang of Four define the template method pattern as follows:

[Its intent is to] 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 et al., 2003, p. 325

Sometimes it is not so much about fixed steps of an algorithm, but about offering a client the possibility of intervention at a certain point. This is called a hook method. Hook methods are normally implemented as empty methods. The client has to decide whether he wants to hook in the process or not.

Since the template method pattern offers a way of altering the behavior of parts of an algorithm, it fits in the category of behavioral patterns. As such it bears a certain resemblance with another behavioral pattern:

  • The strategy pattern provides a means of changing the entire algorithm by using composition and delegation. The template method pattern only aims at changing parts of an algorithm using inheritance and overriding.

Structure of the template method pattern

The template method pattern defines two roles:

  • The AbstractClass which defines the algorithm and the sub-steps.
  • The ConcreteClass which implements the specialization of the sub-steps.

The class implementing the template method doesn’t have to be necessarily abstract, default implementations may exist for each of the steps. In the following class diagram the method runAlgorithm() is the template method running each time the steps one to three. The ConcreteClass decides to override the second step leaving the rest of the behavior unchanged.

UML diagram of the template method pattern.

Examples

Now let’s have a look at a concrete example. Imagine an electric vehicle which we want to drive around.

Acoustic Vehicle Alerting Systems (AVAS) are mandatory in various regions throughout the world meanwhile (at least under certain circumstances), other regions don’t require it.

So, we might have an ElectricVehicle which drives around, and while doing so it has to do some noise. Another car might not have to comply with this regulation. So we define a drive()-method containing the “algorithm” i.e. driving and making noise. While driving around is the same, doing engine noises is something each model implements by itself.

public interface ElectricVehicle {

	default void drive() {
		this.driveAround();
		this.doEngineNoise();
	}
	
	default void driveAround() {
		System.out.println("Drive " + this.getClass().getSimpleName()
			+ " around.");
	}
	
	default void doEngineNoise() {}
}

The car itself implements the interface and overrides the doEngineNoise()-step.

public class MyFancyElectricCar implements ElectricVehicle {

	@Override
	public void doEngineNoise() {
		System.out.println("Rooooaaaar");
	}
	
	public static void main(String[] args) {
		ElectricVehicle electricVehicle = new MyFancyElectricCar();
		electricVehicle.drive();
	}
}

Running the main-method produces the following output.

Drive MyFancyElectricCar around.
Rooooaaaar 

So far, so good. This solution has a series of shortcomings though:

  • MyFancyElectricCar could override the drive()-method, thus changing the algorithm altogether.
  • A client could call driveAround() independently, potentially circumventing legal regulations.

A solution to these problems is to make the template method final and its steps protected. While you could still call the protected methods by mistake from inside the package, no outside client can drive mischievously and inaudibly around the block.

public abstract class AbstractElectricVehicle {

	public final void drive() {
		this.driveAround();
		this.doEngineNoise();
	}
	
	protected abstract void driveAround();
	
	protected void doEngineNoise() {}
}

Should the driving part be also determined entirely by the subclasses, making it abstract forces its implementation.

public class MyOtherFancyElectricCar extends AbstractElectricVehicle {

	@Override
	protected void driveAround() {
		System.out.println("Driving MyOtherFancyElectricCar " + 
			"silently around the block.");
	}

	public static void main(String[] args) {
		AbstractElectricVehicle electricVehicle = new MyOtherFancyElectricCar();
		electricVehicle.drive();
	}
}

Running the main-method leads to the expected output.

Driving MyOtherFancyElectricCar silently around the block.

The template method pattern is a great way to achieve code reuse. Extracting behavior common to multiple classes while providing hooks for varying it, is also a very good example for the Open/Closed principle. As long as the algorithm covers the requirements and the requirements don’t change, the template is closed for changes but still open for necessary adaptations.

Real-life examples of the template method pattern

The template method pattern is one of the fundamental patterns used in many frameworks. One of the most prominent examples is the HttpServlet class whose service-method acts as a template method delegating to the appropriate doXXX()-methods which can be overridden by their subclasses.

All methods calling the abstract read()-method in the class InputStream are template methods, analogous examples are the classes OutputStream, Reader and Writer.

Another very good example of the template pattern method is the runBare()-method of the TestCase class of the JUnit framework. This calls the hook methods setup() and teardown() which are to be implemented by the concrete test cases. The method runTest(), also called by runBare(), won’t be overridden in most cases .


References

Gamma, E., Helm, R., Johnson, R. & Vlissides, J. (2003). Design Patterns. Delhi, India: Pearson Education.


You might also like the following articles:

Petre Sora

Petre Soras Interessen sind vielfältig und befinden sich an der Schnittstelle zwischen Mensch und Informationstechnologie. Als studierter Psychologe und Software Engineer war er sechs Jahre als Java-Entwickler in mehreren Unternehmen tätig. Mit der Gründung der Rezensionsplattform nososo hat er sich entschieden eigene Wege zu gehen.

Schreibe einen Kommentar