Template Pattern

Hyung Jun·2020년 12월 3일
0

Design Pattern

목록 보기
5/10
post-thumbnail

This article is lecture note from "Head First Design Patterns" by Elisabeth Freeman and Kathy Sierra and CS course from Kookmin by Inkyu Kim

Definition

The Template Method Pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

Intent

Imagine we are building a cafe application which serves some kinds of drinks like coffe and tea...
Let have a look at our initial code first.
<Coffee.java>

public class Coffee {
 
	void prepareRecipe() {
		boilWater();
		brewCoffeeGrinds();
		pourInCup();
		addSugarAndMilk();
	}
 
	public void boilWater() {
		System.out.println("Boiling water");
	}
 
	public void brewCoffeeGrinds() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void pourInCup() {
		System.out.println("Pouring into cup");
	}
 
	public void addSugarAndMilk() {
		System.out.println("Adding Sugar and Milk");
	}
}

<Tea.java>

public class Tea {
 
	void prepareRecipe() {
		boilWater();
		steepTeaBag();
		pourInCup();
		addLemon();
	}
 
	public void boilWater() {
		System.out.println("Boiling water");
	}
 
	public void steepTeaBag() {
		System.out.println("Steeping the tea");
	}
 
	public void addLemon() {
		System.out.println("Adding Lemon");
	}
 
	public void pourInCup() {
		System.out.println("Pouring into cup");
	}
}

We noticed that both classes have quite similar look. While the code for dealing with some ingredients are different, the code for behavioural processing and structure is almost identical. Wouldn't it be great to get rid of the code duplication, leaveing the algorithm structure intact?

Structure

The Abstract class declares methods that act as steps of an algorithm, as well as the actual template method which calls these methods in a specific order. The steps may either be declared abstract or have some default implementation. Concrete classes can override all of the steps, but not the template method itself. The concrete class implements the abstract operations, which are called when the templateMethod() needs them.

Source Code

Let's see the code including Template Method Pattern.
<CaffeineBeverageWithHook.java>

public abstract class CaffeineBeverageWithHook {
 
	final 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;
	}
}

This is a abstract class we mentioned above, and prepareRecipe() is our TemplateMethod as you can see. It's declared final to prevent subclasses from reworking the sequence of steps in the algorithm. It defines the sequence of steps, each represented by a method.

brew() and addCondiments() are primitive operations which are must be implemented by concrete subclasses.

boilWater(), pourInCup() and customerWantsCondiments() are concrete operations defined in the abstract class. Especially, customerWantsCondiments() is a method just return true and does nothing else. This is a hook because the subclass can override this method, but doesn't have to.

<CoffeeWithHook.java>

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;
	}
}

This is a concrete class extending our Abstract class we dealt with above.
We can see that this code override the hook and provide its own functionality. Get the user's input on the condiment decision and return true or false. Depending on the input.

Finally, this is a client code.

<BeveerateTestDrive.java>

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();
	}
}

The whole steps are quite simple. Let's wrap up how this process work from begin.

  • Step 1
    Analyze the target algorithm to see whether you can break it into steps. Consider which steps are common to all subclasses and which ones will always be unique.
  • Step 2
    Create the abstract base class and declare the template method and a set of abstract methods representing the algorithm's steps. Outline the algorithm's structure in the template method by executing corresponding steps. Consider making the template method final to prevent subclases from overriding it.
  • Step 3
    It's okay if all the steps end up being abstract. However, some steps might benefit from having a default implementation. Subclasses don't have to implement those methods.
  • Step 4
    Thinking of adding hooks between the crucial steps of the algorithm.
  • Step 5
    For each variation of the algorithm, create a new concrete subclass. It must implement all of the abstract steps, but may also override some of the optional ones.

Why is it good? 🧐

This pattern is useful anytime you have an algorithm that has one or more steps that need to vary, or when you have a couple algorithms that are very nearly the same but differ by just a step or two. Rather than having multiple classes that implement a nearly identical set of instructions, or trying to mash up methods that are really different responsibilities into one class, you can cleanly seperate concerns into their own implementations.
It is good to use this pattern not just because you think part of an algorithm might one day need to change, if a new requirement ever actually arrives, only then consider breaking out the implementaion into abstract and concrete classes.

The Template Method is used prominently in frameworks. Each framework implements the invariant pieces of a domain's architecture, and defines "placeholders" for all necessary or interesting client customisation options. In doing so, the framework becomes the "centre of the universe", and the client customisations are simply "the third rock from the sun". This inverted control structure has been affectionately labelled "the Hollywood principle" - "Don't call us, we'll call you."

Hollywood Principle 🎥🇺🇸

The Hollywood principle is the design principle that is summarise by Don't call us, we'll call you. It comes from the Hollywood philosophy where production houses call actors if there is any role for the actor.

In the object-oriented world, we allow low-level components to hoook themselves into the system with Hollywood Principle. However, the high-level components determine how the low-level systems are needed and when they are needed. In other words, high-level comoponents treat low-level components as Don't call us, we'll call you.
This relates to the Template Method pattern in the sense that it's high-level abstract class that arranges the steps to define the algorightm.

									_from oreilly.com_
profile
Keep Learning👨🏻‍💻

0개의 댓글