What Is The Template Method Design Pattern?


3 min read

There are still elements about this design method I am unsure of, so please feel free to leave feedback.

In software engineering, the template method pattern is a behavioural design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm’s structure. Wikipedia

And in case you were wondering what a behavioural design pattern is:

Behavioural design patterns are design patterns that identify common communication patterns among objects and realise these patterns. By doing so, these patterns increase flexibility in carrying out this communication. Wikipedia

My understanding is within an abstract class (base class), the template method outlines the steps of an algorithm as a concrete method.

Within the method, it calls abstract (primitive operations) and concrete methods (concrete operations) in the class, in a particular order.

The concrete methods called within a template method represents the parts of the algorithm which are standard. On the other hand, the abstract methods called represents the customisable aspects. A template method can also consist of abstract methods only.

Concrete classes, which extend from the base class, need to implement the abstract methods. As a result, this provides some customisation across the subclasses.

The abstract class can also contain hook methods, which the subclasses can override. Hook methods are explained in more detail further on in this blog post.

One of the purposes of the template method design pattern is to avoid code duplication. When implementing the pattern, the number of abstract methods to implement should be kept to a minimum.


Implementing The Template Design Pattern

Below is an example, demonstrating the use of the template design pattern. It consist of a base class called Greeter and the subclasses EnglishGreeter and FrenchGreeter.

The Greeter Class

Greeter is an abstract class. It contains a concrete method called greeting(). This is the template method.

It also contains two abstract methods, doHello() and doGoodbye(). Subclasses of Greeter should implement the abstract methods.

1public abstract class Greeter {
2 public final void greeting() {
3 doHello();
4 doGoodbye();
5 }
6
7 public abstract void doHello();
8
9 public abstract void doGoodbye();
10}

The EnglishGreeter Class

This class is a subclass of Greeter. It implements the abstract methods doHello() and doGoodbye().

1public class EnglishGreeter extends Greeter {
2 @Override
3 public void doHello() {
4 System.out.println("Hello!");
5 }
6
7 @Override
8 public void doGoodbye() {
9 System.out.println("Goodbye!");
10 }
11}

The FrenchGreeter Class

This class is also a subclass of Greeter and implements the abstract methods.

1public class FrenchGreeter extends Greeter {
2 @Override
3 public void doHello() {
4 System.out.println("Bonjour!");
5 }
6
7 @Override
8 public void doGoodbye() {
9 System.out.println("Au revoir!");
10 }
11}

EnglishGreeter and FrenchGreeter have also inherited the greeting() method from Greeter.

Creating an instance of EnglishGreeter and calling the greeting() method, prints the following to the console:

1Hello!
2Goodbye!

Creating an instance of FrenchGreeter and calling the greeting() method, prints the following to the console:

1Bonjour!
2Au revoir!

Although doHello() and doGoodbye() are called in a particular order, the method bodies contain different content in EnglishGreeter and FrenchGreeter, resulting in different output.

Here is a UML diagram, outlining the Greeter example:


The Template Method with Concrete Methods

Let’s change Greeter to include the concrete methods, waveHello() and waveGoodbye(). Both methods prints an emoji. Greeting() is amended to call the new methods.

1public abstract class Greeter {
2 public final void greeting() {
3 waveHello();
4 doHello();
5 waveGoodbye();
6 doGoodbye();
7 }
8
9 public void waveHello() {
10 System.out.println("πŸ™Œ");
11 }
12
13 public abstract void doHello();
14
15 public void waveGoodbye() {
16 System.out.println("πŸ‘‹οΈ");
17 }
18
19 public abstract void doGoodbye();
20}

As the new methods are concrete, EnglishGreeter and FrenchGreeter do not need to implement them.

Creating an instance of EnglishGreeter and calling the greeting() method, prints the following to the console:

1πŸ™Œ
2Hello!
3πŸ‘‹οΈ
4Goodbye!

Creating an instance of FrenchGreeter and calling the greeting() method, prints the following to the console:

1πŸ™Œ
2Bonjour!
3πŸ‘‹οΈ
4Au revoir!

The emojis are consistent throughout both classes, and the variation of text outputted still remains. Here is a UML diagram, outlining this example:


Adding Hook Methods

A hook method can be defined as an optional method in the base class. It provides default behaviour, which subclasses can extend, if necessary. It is usually empty and does nothing by default.

For example, the Greeter class now has an optional method, called celebrate, which is empty.

1public abstract class Greeter {
2 public final void greeting() {
3 waveHello();
4 doHello();
5 celebrate();
6 waveGoodbye();
7 doGoodbye();
8 }
9
10 public void waveHello() {
11 System.out.println("πŸ™Œ");
12 }
13
14 public abstract void doHello();
15
16 public void celebrate() {}
17
18 public void waveGoodbye() {
19 System.out.println("πŸ‘‹οΈ");
20 }
21
22 public abstract void doGoodbye();
23}

In the subclasses, celebrate() can be overridden to edit the method. In the example below, EnglishGreeter is overriding the celebrate method to print out "WOO HOO!"

1public class EnglishGreeter extends Greeter {
2 @Override
3 public void doHello() {
4 System.out.println("Hello!");
5 }
6
7 @Override
8 public void celebrate() {
9 System.out.println("WOO HOO!");
10 }
11
12 @Override
13 public void doGoodbye() {
14 System.out.println("Goodbye!");
15 }
16}

Creating an instance of EnglishGreeter and calling the greeting() method, prints the following to the console:

1πŸ™Œ
2Hello!
3WOO HOO!
4πŸ‘‹οΈ
5Goodbye!

The celebrate method has not been overridden in the FrenchGreeter subclass. As a result, there are no changes to what is printed when calling FrenchGreeter's greeting method.

1πŸ™Œ
2Bonjour!
3πŸ‘‹οΈ
4Au revoir!

Here is a UML diagram, outlining the example above:


Resources:

Previous post:
What Are Interfaces, Abstract And Concrete Classes?
Next post:
What Is Abstraction In Object-Oriented Programming?

Discussion