Template Method Design Pattern

Intent

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.

Structure

Template method design pattern structure

Image : Template method design pattern structure

Participants

1. AbstractClass
  • defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm.
  • implements a template method defining the skeleton of an algorithm.The template method calls primitive operations as well as operations defined in AbstractClass or those of other objects. 
2. ConcreteClass 
  • implements the primitive operations to carry out subclass-specific steps of the algorithm.

Collaborations

  • ConcreteClass relies on AbstractClass to implement the invariant steps of the algorithm.

Source Code

Follow the steps to implementation of Template design pattern.
Let's create a coffee beverage system
Step 1: Create CaffeineBeverage class ,which defines skeleton for the algorithm.
/**
 * CaffeineBeverage defines skeleton for the algorithm.
 * @author RAMESH
 *
 */
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 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 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 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 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

Important points about template method pattern

  • Template method in super class follows “the Hollywood principle”: “Don’t call us, we’ll call you”. This refers to the fact that instead of calling the methods from base class in the subclasses, the methods from subclass are called in the template method from superclass.
  • Template method in super class should not be overriden so make it final.
  • Customization hooks:Methods containing a default implementation that may be overidden in other classes are called hooks methods. Hook methods are intended to be overridden, concrete methods are not.So in this pattern,we can provide hooks methods.The problem is sometimes it becomes very hard to differentiate between hook methods and concrete methods.
  • Template methods are technique for code reuse because with this,you can figure out common behavior and defer specific behavior to subclasses.

Applicability

The Template Method pattern should be used
  • 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 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.

Credits

Comments