Template Pattern from Head First Design Patterns

In this post, we will learn Template Method Pattern implementation with examples from Head First Design Patterns Book.
Table of contents
  1. Overview
  2. Class Diagram
  3. Implementation with Example
  4. Conclusion (Source code on Github Repository)
  5. References

1. Overview

Template Method Pattern defined
The Template Method Pattern defines 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.

1.1 When to use template method pattern

  • To implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary.
  • When common behavior among subclasses should be factored and localized in a common class to avoid code duplication. This is a good example of "refactoring to generalize" as described by Opdyke and Johnson. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations.
  • To control subclasses extensions. You can define a template method that calls "hook" operations at specific points, thereby permitting extensions only at those points.
The Hollywood Principle
Don't call us, we will call you.
The connection between the Hollywood Principle and Template Method pattern is probably somewhat apparent: when we design with Template Method Pattern, we are telling subclasses, "don't call us, we'll call you."
This connection between the Hollywood Principle and Template Method pattern explained in the example.

2. Class Diagram

Template method design pattern structure


3. Implementation with Example


Let's implement Coffee Beverage example from Head First Design Patterns Book.
Coffee Beverage System provides two features :
  1. StarBuzz Coffee Recipe
  2. StarBuzz Tea Recipe
StarBuzz Coffee Recipe Let's start with the recipe.
1. Boil some water

2. Brew coffee in boiling water

3. Pour coffee in cup

4. Add sugar and milk
StarBuzz Tea Recipe Let's start with the recipe.
1. Boil some water

2. Steep tea in boiling water

3. Pour tea in cup

4. Add lemon
Notice that both recipes follow same algorithms:
Let's write template method prepareRecipe() method :
final void prepareRecipe() {
  boilWater();
  brew();
  pourInCup();
  addCondiments();
 }


Follow the steps to implementation of a Template design pattern.
Let's create a coffee beverage system
Step 1: Create CaffeineBeverage class, which defines a skeleton for the algorithm.

public abstract class CaffeineBeverage {
  
 final void prepareRecipe() {
  boilWater();
  brew();
  pourInCup();
  addCondiments();
 }
 
 abstract void brew();
  
 abstract void addCondiments();
 
 void boilWater() {
  System.out.println("Boiling water");
 }
  
 void pourInCup() {
  System.out.println("Pouring into cup");
 }
}
Step 2: Create a Coffee class, which extends an abstract CaffeineBeverage class and implements abstract methods.
public class Coffee extends CaffeineBeverage {
 public void brew() {
  System.out.println("Dripping Coffee through filter");
 }
 public void addCondiments() {
  System.out.println("Adding Sugar and Milk");
 }
}
Step 3: Create a Tea class which extends an abstract CaffeineBeverage class and implements abstract methods.
public class Tea extends CaffeineBeverage {
 public void brew() {
  System.out.println("Steeping the tea");
 }
 public void addCondiments() {
  System.out.println("Adding Lemon");
 }
}
Step 4: With a hook, we can override a method or not, It's our choice, If we don't then abstract class provides a default implementation.
public abstract class CaffeineBeverageWithHook {
 
 void prepareRecipe() {
  boilWater();
  brew();
  pourInCup();
  if (customerWantsCondiments()) {
   addCondiments();
  }
 }
 
 abstract void brew();
 
 abstract void addCondiments();
 
 void boilWater() {
  System.out.println("Boiling water");
 }
 
 void pourInCup() {
  System.out.println("Pouring into cup");
 }
 
 boolean customerWantsCondiments() {
  return true;
 }
}
Step 5: Let's override the hook() method and provide the own implementation.
import java.io.*;

public class CoffeeWithHook extends CaffeineBeverageWithHook {
 
 public void brew() {
  System.out.println("Dripping Coffee through filter");
 }
 
 public void addCondiments() {
  System.out.println("Adding Sugar and Milk");
 }
 
 public boolean customerWantsCondiments() {

  String answer = getUserInput();

  if (answer.toLowerCase().startsWith("y")) {
   return true;
  } else {
   return false;
  }
 }
 
 private String getUserInput() {
  String answer = null;

  System.out.print("Would you like milk and sugar with your coffee (y/n)? ");

  BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
  try {
   answer = in.readLine();
  } catch (IOException ioe) {
   System.err.println("IO error trying to read your answer");
  }
  if (answer == null) {
   return "no";
  }
  return answer;
 }
}
import java.io.*;

public class TeaWithHook extends CaffeineBeverageWithHook {
 
 public void brew() {
  System.out.println("Steeping the tea");
 }
 
 public void addCondiments() {
  System.out.println("Adding Lemon");
 }
 
 public boolean customerWantsCondiments() {

  String answer = getUserInput();

  if (answer.toLowerCase().startsWith("y")) {
   return true;
  } else {
   return false;
  }
 }
 
 private String getUserInput() {
  // get the user's response
  String answer = null;

  System.out.print("Would you like lemon with your tea (y/n)? ");

  BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
  try {
   answer = in.readLine();
  } catch (IOException ioe) {
   System.err.println("IO error trying to read your answer");
  }
  if (answer == null) {
   return "no";
  }
  return answer;
 }
}
Step 6: Test the above implementation.
Let's create a hot coffee and hot tea, here customer needs to decide whether he/she wants condiment by an input.
public class BeverageTestDrive {
 public static void main(String[] args) {
 
  Tea tea = new Tea();
  Coffee coffee = new Coffee();
 
  System.out.println("\nMaking tea...");
  tea.prepareRecipe();
 
  System.out.println("\nMaking coffee...");
  coffee.prepareRecipe();

 
  TeaWithHook teaHook = new TeaWithHook();
  CoffeeWithHook coffeeHook = new CoffeeWithHook();
 
  System.out.println("\nMaking tea...");
  teaHook.prepareRecipe();
 
  System.out.println("\nMaking coffee...");
  coffeeHook.prepareRecipe();
 }
}
Output :
Making tea...
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon

Making coffee...
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Sugar and Milk

Making tea...
Boiling water
Steeping the tea
Pouring into cup
Would you like lemon with your tea (y/n)? y
Adding Lemon

Making coffee...
Boiling water
Dripping Coffee through filter
Pouring into cup
Would you like milk and sugar with your coffee (y/n)? y
Adding Sugar and Milk

4. Conclusion

In this post, we have learned the Template Method Pattern from Head First Design Patterns book. There is a separate post for Factory Pattern in detail with examples, advantages, real-world examples.
All the source code for this post available on Github Repo. This is an eclipse project so you can import into your workspace and play with it.

Comments