定义
模板方法模式,在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
实例
以星巴克咖啡师训练手册为例。
星巴克咖啡冲泡法:
(1)把水煮沸
(2)用沸水冲泡咖啡
(3)把咖啡倒进杯子
(4)加糖和牛奶
星巴克茶冲泡法:
(1)把水煮沸
(2)用沸水浸泡茶叶
(3)把茶倒进杯子
(4)加柠檬
咖啡和茶的冲泡法大致上一样的。如果分开实现咖啡和茶的冲泡方法,则会发现有不少重复代码。既然咖啡和茶是如此地相似,我们应该将共同的部分抽取出来,放进一个基类中。
// 统称为咖啡因饮料
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");
}
}
咖啡具体实现:
public class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("Drepping Coffee through filter");
}
public void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
茶具体实现:
public class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("Steeping the tea");
}
public void addCondiments() {
System.out.println("Adding Lemon");
}
}
对模板方法进行挂钩
钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。
例如,对咖啡因饮料进行挂钩:
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;
}
}
小结
-
模板方法定义了算法的步骤,把这些步骤的实现延迟到子类。
-
模板方法模式为我们提供了一个代码复用的重要技巧。
-
模板方法的抽象类可以定义具体方法、抽象方法和钩子。
-
抽象方法由子类实现。
-
钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它。
-
为了防止子类改变模板方法中的算法,可以将模板方法声明为final。
-
好莱坞原则告诉我们,将决策权放在高层模板中,以便决定如何以及何时调用底层模块。
-
你将在真实世界代码中看到模板方法模式的许多变体,不要期待它们全都是一眼就可以被你认出的。
-
策略模式和模板方法模式都封装算法,一个用组合,一个用继承。
-
工厂方法是模板方法的一种特殊版本。