茶和咖啡是两个功能类似的类:咖啡的制作包括boil water, brew, pour, add condiments四步,而沏茶的过程包括boil water, steep, pour, add lemon四步。
它们的一,三两步相同,二,四两步类似,如下:
class Coffee {
public void boilWater() {
System.out.println("Boiling water");
}
public void brew() {
System.out.println("Brewing Coffee in the water");
}
public void pour() {
System.out.println("Pour Coffee in cup");
}
public void addCondiments() {
System.out.println("Add sugar and Milk in the coffee");
}
}
class Tea {
public void boilWater() {
System.out.println("Boiling water");
}
public void steep() {
System.out.println("Steep te a in the water");
}
public void pour() {
System.out.println("Pour tea in cup");
}
public void addLemon() {
System.out.println("Add lemon in the tea");
}
}
为了减少重复,增加复用,我们给它们加一个共同的抽象父类:CaffeineBeverage。
两个子类中相同的部分移到了父类中,变成父类的方法;不同的部分在父类中写成抽象方法,具体实现放到子类中(对比工厂方法,工厂方法是把对象的创建方法放到了子类中)。
另外,添加了一个prepareRecipe方法,相当于“一键”功能(这个和façade pattern的一键功能是有区别的,这里“一键”是写在抽象父类中,而在façade pattern里,是写在客户端和类库之间的一个新增的类中)。如下:
package javaapplication36;
public class Main {
public static void main(String[] args) {
CaffeineBeverage beverage = new Coffee();
beverage.prepareRecipe();
beverage = new Tea();
beverage.prepareRecipe();
}
}
abstract class CaffeineBeverage {
public void prepareRecipe() {
boilWater();
brew();
pour();
addCondiments();
}
public void boilWater() {
System.out.println("Boiling water");
}
public abstract void brew();
public void pour() {
System.out.println("Pour into the cup");
}
public abstract void addCondiments();
}
class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("Brewing Coffee in the water");
}
public void addCondiments() {
System.out.println("Add sugar and Milk in the coffee");
}
}
class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("Steep the tea");
}
public void addCondiments() {
System.out.println("Add lemon in the tea");
}
}
在以上代码中:父类的prepareRecipe方法实际上就是所谓的Template 方法,template方法中所调用的其他函数,有的在父类中已经实现,有的会根据不同的子类,而采用不同的实现。
Template pattern:即算法(prepareRecipe方法)在父类中写好了,会调用父类里的各种方法来实现(boilingwater,brew,pour,addCondiments),这些方法有的会在父类中就已经实现,有的会放到子类中实现之。
再把程序写的更完善些:由于算法prepareRecipe是不会变动的,所以我们给它加上final,而对于addCondiments ,有的饮料有可能需要配料,而有的饮料根本不可能添加配料。(也就是说有的子类, prepareRecipe方法中需要执行addCondiments功能,而有的子类不需要。)
为了不写几种重复prepareRecipe方法(prepareRecipe1, prepareRecipe2….每种方法只是有个别步骤不一样),我们使用Hook方式来做(Hook就是指一个包含空内容的实体方法,不同于抽象方法),如下:
package javaapplication36;
public class Main {
public static void main(String[] args) {
CaffeineBeverage beverage = new Coffee();
beverage.prepareRecipe();
beverage = new Tea();
beverage.prepareRecipe();
beverage = new Milk();
beverage.prepareRecipe();
}
}
abstract class CaffeineBeverage {
public void prepareRecipe() {
boilWater();
brew();
pour();
addCondiments();
}
public void boilWater() {
System.out.println("Boiling water");
}
public abstract void brew();
public void pour() {
System.out.println("Pour into the cup");
}
public void addCondiments() { //HOOK,一个空方法,不做任何事情,继承的子类根据需//要覆盖或者保持它为空。
}
}
class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("Brewing Coffee in the water");
}
public void addCondiments() { //对addCondiments重写
System.out.println("Add sugar and Milk in the coffee");
}
}
class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("Steep the tea");
}
public void addCondiments() { //对addCondiments重写
System.out.println("Add lemon in the tea");
}
}
class Milk extends CaffeineBeverage { //没有对addCondiments重写
public void brew() {
System.out.println("Brewing Milk in the water.");
}
}
最后,我们试图更进一步完善代码,即使子类有addCondiments这个方法,我们也可以根据客户端的需要,选择在执行prepareRecipe的时候是否进行addCondiments这一步。
package javaapplication36;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
public static void main(String[] args) {
CaffeineBeverage beverage = new Coffee();
beverage.prepareRecipe();
beverage = new Tea();
beverage.prepareRecipe();
beverage = new Milk();
beverage.prepareRecipe();
}
}
abstract class CaffeineBeverage {
public void prepareRecipe() {
boilWater();
brew();
pour();
if (customerWantCondiments()) {
addCondiments();
}
}
public void boilWater() {
System.out.println("Boiling water");
}
public abstract void brew();
public void pour() {
System.out.println("Pour into the cup");
}
public void addCondiments() {
}
public boolean customerWantCondiments() {
return true;//相当于不做任何判断就直接让prepareRecipe执行addCondiments()
}
}
class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("Brewing Coffee in the water");
}
@Override
public void addCondiments() {
System.out.println("Add sugar and Milk in the coffee");
}
@Override
public boolean customerWantCondiments() {
String answer = getUserInput();
if (answer.startsWith("y")) {
return true;
}
else {
return false;
}
}
private String getUserInput() { //获取输入,确定是否要Condiments
System.out.println("Would you love some condiments? (y/n)");
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String answer = null;
try {
answer = in.readLine();
}
catch (IOException ex) {
Logger.getLogger(Coffee.class.getName()).log(Level.SEVERE, null, ex);
}
answer = answer.toLowerCase();
return answer;
}
}
class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("Steep the tea");
}
@Override
public void addCondiments() {
System.out.println("Add lemon in the tea");
}
}
class Milk extends CaffeineBeverage {
public void brew() {
System.out.println("Brewing Milk in the water.");
}
}
再举一例,很常见的,自定义类型数组的比较就是用到了Template pattern,如下:
package javaapplication37;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Duck[] ducks = {new Duck("duck1", 10), new Duck("duck2", 13), new Duck("duck3", 9)};
displayDucks(ducks);
Arrays.sort(ducks);//Arrays相当于template pattern中的抽象父类,sort是该类中的//算法函数
displayDucks(ducks);
}
private static void displayDucks(Duck[] ducks) {
for (int i = 0; i < ducks.length; i++) {
System.out.println(ducks[i].toString());
}
}
}
class Duck implements Comparable {
String name;
int weight;
Duck(String name, int weight) {
this.name = name;
this.weight = weight;
}
@Override
public String toString() {
return name + ":" + weight;
}
public int compareTo(Object o) { //compareTo方法相当于HOOK
Duck otherDuck = (Duck) o;
if (this.weight < otherDuck.weight) {
return -1;
}
else if (this.weight == otherDuck.weight) {
return 0;
}
else {
return 1;
}
}
}
我们来看看Arrays这个类是如何使用template pattern的。
public class Arrays {
…
…
public static void sort(Object[] a) { //sort是算法函数
Object[] aux = (Object[])a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
private static void mergeSort(Object[] src,
Object[] dest,
int low,
int high,
int off) {
int length = high - low;
// Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
for (int i=low; i<high; i++)
for (int j=i; j>low &&
((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
swap(dest, j, j-1);
return;
}
…
…
}
}
public interface Comparable<T> {
public int compareTo(T o); //compareTo是HOOK
}
再举一例,看看Swing中的JFrame是如何使用template pattern的:
package javaapplication38;
import java.awt.Graphics;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
MyFrame frame = new MyFrame("my first frame");
}
}
class MyFrame extends JFrame { //JFrame 相当于template pattern中的父类,而它里面的//update方法就是其算法函数
public MyFrame(String title) {
super(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(300, 300);
this.setVisible(true);
}
public void paint(Graphics g) { //paint是HOOK
super.paint(g);
String msg = "i rule";
g.drawString(msg, 100, 50);
}
}