08 - Template Method Pattern
08 - Template Method Pattern
Encapsulating Algorithms
Taking encapsulation
to a deeper new level
It's time for some more caffeine
Some people can't live without their coffee;
some people can't live without their tea!
--> the common ingredient:
CAFFEINE of course!
2
Whipping up some
coffee and tea classes
public class Coffee {
void prepareRecipe() { Let's play "coding barista" and write
boilWater();
some code for making coffee and tea.
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk(); <-- Here's coffee…
}
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");
}
}
3
and now the Tea…
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");
}
}
4
Design Puzzle
YOU'VE SEEN THAT THE COFFEE AND TEA CLASSES HAVE A FAIR BIT OF CODE DUPLICATION. TAKE
ANOTHER LOOK AT THE COFFEE AND TEA CLASSES AND DRAW A CLASS DIAGRAM SHOWING HOW YOU'D
REDESIGN THE CLASSES TO REMOVE REDUNDANCY.
public class Coffee { public class Tea {
void prepareRecipe() { void prepareRecipe() {
boilWater(); boilWater();
brewCoffeeGrinds(); steepTeaBag();
pourInCup(); pourInCup();
addSugarAndMilk(); addLemon();
} }
public void boilWater() { public void boilWater() {
System.out.println("Boiling water"); System.out.println("Boiling water");
} }
public void brewCoffeeGrinds() { public void steepTeaBag() {
System.out.println("Dripping Coffee through filter"); System.out.println("Steeping the tea");
} }
public void pourInCup() { public void addLemon() {
System.out.println("Pouring into cup"); System.out.println("Adding Lemon");
} }
public void addSugarAndMilk() { public void pourInCup() {
System.out.println("Adding Sugar and Milk"); System.out.println("Pouring into cup");
} }
} }
5
Sir, may I abstract your Coffee, Tea?
8
Abstracting prepareRecipe()
2. Now we have a new
prepareRecipe()
method, but we need
public abstract class CaffeineBeverage { to fit it into the code.
final void prepareRecipe() {
boilWater(); To do this we are
brew(); going to start with
pourInCup(); the CaffeineBeverage
addCondiments(); superclass:
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println(“Boiling water”);
}
void pourInCup() {
System.out.println(“Pouring into cup”);
}
} 9
Abstracting prepareRecipe()
3. Finally we need to deal with the Coffee and Tea classes. They now rely on CaffeineBeverage to
handle the recipe, so they just need to handle brewing and condiments:
11
What have we done?
12
Meet the
Template Method
CaffeineBeverage
class contains actual
"template method"
13
Let's make some tea…
Let's step through making a tea and trace through how the template method works. You'll see that the
template method controls the algorithm; at certain points in the algorithm, it lets the subclass supply the
implementation of the steps...
1. Okay, first we need a Tea object…
Tea myTea = new Tea();
2. Then we call the template method:
myTea.prepareRecipe ();
which follows the algorithm for making caffeine beverages…
4. Next we need to brew the tea, which only the subclass knows how to do:
brew();
14
Continue making the tea…
15
What did the Template Method get us?
Underpowered Tea & Coffee New, hip CaffeinBeverage powered
implementation by Template Method
Coffee and Tea are running the show; they The CaffeineBeverage class runs the show; it
control the algorithm. has the algorithm, and protects it.
Code is duplicated across Coffee and Tea. The CaffeineBeverage class maximizes reuse
among the subclasses.
Code changes to the algorithm require opening The algorithm lives in one place and code
the subclasses and making multiple changes. changes only need to be made there.
Classes are organized in a structure that The Template Method version provides a framework that
requires a lot of work to add a new caffeine other caffeine beverages can be plugged into. New caffeine
beverage. beverages only need to implement a couple of methods.
16
Template Method Pattern defined
This pattern is all about creating a
template for an algorithm.
17
Code Up Close
void concreteOperation() {
// implementation here
}
}
18
Code Way Up Close
abstract class AbstractClass {
final void templateMethod() {
primitiveOperation1();
Now we're going to look even primitiveOperation2();
closer at the types of method concreteOperation();
that can go in the abstract hook();
class. }
19
Hooked on Template Method…
public abstract class CaffeineBeverageWithHook {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
} ▪ A hook is a method that is
abstract void brew(); declared in the abstract class,
abstract void addCondiments(); but only given an empty or
void boilWater() { default implementation.
System.out.println(“Boiling water”); ▪ This gives subclasses the
} ability to "hook into" the
void pourInCup() { algorithm at various points, if
System.out.println(“Pouring into cup”); they wish; a subclass is also
free to ignore the hook.
}
boolean customerWantsCondiments() { ▪ There are several uses of
return true; hooks; let's take a look at one
} now, we'll talk about a few
other uses later:
} 20
public class CoffeeWithHook extends CaffeineBeverageWithHook {
Using the hook public void brew() {
System.out.println(“Dripping Coffee through filter”);
}
public void addCondiments() {
▪ To use the hook, we override it in System.out.println(“Adding Sugar and Milk”);
our subclass. }
public boolean customerWantsCondiments() {
▪ Here, the hook controls whether String answer = getUserInput();
if (answer.toLowerCase().startsWith(“y”)) {
CaffeineBeverage adds a condiment return true;
to the beverage. } else {
return false;
▪ How do we know whether the }
}
System.out.println(“\nMaking tea...”);
teaHook.prepareRecipe();
System.out.println(“\nMaking coffee...”);
coffeeHook.prepareRecipe();
}
}
22
there are no
Dumb Questions
Q: When I'm creating a template method, how do I know when Q: Does a subclass have to implement all the abstract
to use abstract methods and when to use hooks? methods in the AbstractClass?
A: Use abstract methods when your subclass MUST provide A: Yes, each concrete subclass defines the entire set of
an implementation of the method or step in the algorithm. abstract methods and provides a complete implementation of
Use hooks when that part of the algorithm is OPTIONAL. the undefined steps of the template method's algorithm.
With hooks, a subclass may choose to implement that hook,
Q: It seems like I should keep my abstract methods small in
but it doesn't have to.
number, otherwise it will be a big job to implement them in the
Q: What are hooks really supposed to be used for? subclass.
A: There are a few uses of hooks. As we just said, a hook A: That's a good thing to keep in mind when you write template
may provide a way for a subclass to implement an optional methods. Sometimes this can be done by not making the
pad of an algorithm, or if it isn't important to the subclass' steps of your algorithm too granular. But it's obviously a
implementation, it can skip it. Another use is to give the trade off: the less granularity, the less flexibility. Remember,
subclass a chance to react to some step in the template too, that some steps will be optional; so you can implement
method that is about to happen, or just happened. For these as hooks rather than abstract classes, easing the
instance, a hook method like justReOrderedList() burden on the subclasses of your abstract class.
allows the subclass to perform some activity (such as
redisplaying an onscreen representation) after an internal
list is reordered.
23
The Hollywood Principle
25
? WHO DOES WHAT ?
Match each pattern with its description
26
Template Methods in the Wild
▪ The Template Method Pattern is a very common pattern and
you're going to find lots of it in the wild.
▪ You've got to have a keen eye, though, because there are many
implementations of the template methods that don't quite look
like the textbook design of the pattern.
▪ This pattern shows up so often because it's a great design
tool for creating frameworks, where the framework controls
how something gets done, but leaves you (the person using the
framework) to specify your own details about what is actually
happening at each step of the framework's algorithm.
▪ Let's take a little safari through a few uses in the wild (well,
okay, in the Java API)...
27
Sorting with Template Method
What's something we often need to do with arrays? Sort them!
private static void mergeSort(Object src[], Object dest[], int low, int high, int off) {
for (int i=low; i<high; i++){
for (int j=i; j>low && ((Comparable)dest[j-1]).compareTo((Comparable)dest[j])>0; j--) {
swap(dest, j, j-1);
}
}
return;
}
28
We have some ducks to sort…
Let's say you have an array of ducks that you'd like to sort. How do you do it?
Well, the sort template method in Arrays gives us the algorithm, but you need to tell
it how to compare ducks, which you do by implementing the compareTo() method...
Make sense?
Good point.
Here's the deal: the designers of sort() wanted it to be useful
across all arrays, so they had to make sort() a static method
that could be used from anywhere.
But that's okay, it works almost the same as if it were in a superclass. Now, here is one more detail:
because sort() really isn't defined in our superclass, the sort() method needs to know that you've
implemented the compareTo() method, or else you don't have the piece needed to complete the sort
algorithm. To handle this, the designers made use of the Comparable interface. All you have to do is
implement this interface, which has one method compareTo().
29
What is CompareTo()?
▪ The compareTo() method compares two objects and returns whether one is less than, greater than, or
equal to the other.
▪ sort() uses this as the basis of its comparison of objects in the array.
30
Comparing Ducks
public class Duck implements Comparable {
String name; with Ducks
int weight; Okay, so you know that if you
public Duck(String name, int weight) {
want to sort Ducks, you're
this.name = name; this.weight = weight; going to have to implement
} this compareTo() method;
public String toString() { by doing that you'll give the
return name + “ weighs “ + weight; Arrays class what it needs
}
to complete the algorithm and
public int compareTo(Object object) { sort your ducks.
Duck otherDuck = (Duck)object;
if (this.weight < otherDuck.weight) {
return -1;
} else if (this.weight == otherDuck.weight) {
return 0;
} else { // this.weight > otherDuck.weight
return 1;
}
}
}
31
public class DuckSortTestDrive { Let's sort some Ducks ..
public static void main(String[] args) {
Duck[] ducks = { new Duck(“Daffy”, 8),
TestDrive
new Duck(“Dewey”, 2),
new Duck(“Howard”, 7),
new Duck(“Louie”, 2),
new Duck(“Donald”, 10),
new Duck(“Huey”, 2) };
System.out.println(“Before sorting:”);
display(ducks);
Arrays.sort(ducks);
System.out.println(“\nAfter sorting:”);
display(ducks);
}
public static void display(Duck[] ducks) {
for (int i = 0; i < ducks.length; i++) {
System.out.println(ducks[i]);
}
}
} 32
Let the sorting commence!
33
The making of the sorting duck machine
Let's see how the Arrays sort() template method works. We'll check out how the template method
controls the algorithm, and at certain points, how it asks Ducks to supply the implementation of a step...
1. First, we need an array of Ducks:
Duck[] ducks = {new Duck("Daffy, 8", ...)};
2. Then we call the sort() template method in the Arrays
class and pass it our ducks:
Arrays.sort(ducks);
The sort() method control the sort procedure…
3. To sort an array, you need to compare two items one by one until
the entire list is in sorted order. When it comes to comparing two
ducks, the sort method relies on the Duck's compareTo() method
to know how to do this. The compareTo() method is called on the
first duck and passed the duck to be compared to:
ducks[0].compareTo(ducks[1]);
34
continue making of the sorting duck machine…
4. If the Ducks are not in sorted order; they're swapped with the
concrete swap() method in Arrays:
swap()
35
Swinging with Frames
If you haven't encountered
JFrame, it's the most basic
Swing container and inherits a
paints method. By default,
paints does nothing because public class MyFrame extends JFrame {
it's a hook! By overriding public MyFrame(String title) {
paints, you can insert yourself super(title);
into JFrame's algorithm for this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(300,300);
displaying its area of the this.setVisible(true);
screen and have your own }
graphic output incorporated public void paint(Graphics graphics) {
into the JFrame. Here's an super.paint(graphics);
embarrassingly simple example String msg = “I rule!!”;
graphics.drawString(msg, 100, 100);
of using a JFrame to override }
the paints hook method: public static void main(String[] args) {
MyFrame myFrame = new MyFrame(“Design Patterns”);
}
} 36
Tools for your Design Toolbox
Bullet Points
▪ A "template method" defines the steps of an algorithm, deferring to
subclasses for the implementation of those steps.
▪ The Template Method Pattern gives us an important technique for code
reuse.
▪ The template method's abstract class may define concrete methods,
abstract methods and hooks.
▪ Abstract methods are implemented by subclasses.
▪ Hooks are methods that do nothing or default behavior in the abstract
class, but may be overridden in the subclass.
▪ To prevent subclasses from changing the algorithm in the template
method, declare the template method as final.
▪ The Hollywood Principle guides us to put decision-making in high-level
modules that can decide how and when to call low level modules.
▪ You'll see lots of uses of the Template Method Pattern in real world
code, but don't expect it all (like any pattern) to be designed "by the
book."
▪ The Strategy and Template Method Patterns both encapsulate
algorithms, one by inheritance and one by composition.
▪ The Factory Method is a specialization of Template Method.
37