设计模式(八)模板方法模式
模板方法模式
如果你希望封装一套方法,你可能会想到策略模式。因此我们从这里开始。
策略模式
策略模式使用组合和委派的方式,先讲一系列算法的实现封装在一组接口中,然后让可以使用某种算法的类保存一个相应的接口的实例。这样当类的对象想要执行算法时,就可以调用其中的接口实例内封装的算法。
当每个算法都代表一个独立的过程时,策略模式更加合理,但如果一些算法实际上都是某一个大的算法的分解步骤时,且一些分解步骤仅有一种实现时,策略模式就会造成一些代码重复。
距离来说,对于咖啡因饮料的冲泡过程,茶饮和咖啡的不同仅在于冲泡不同的原料以及添加不同的调料,煮沸水和倒水的过程是相同的。
模板方法模式
因此,可以将完全相同的算法在抽象基类中实现,其余方法留作抽象,交由子类实现。同时可以用final将冲泡流程固定,避免子类破坏这一过程的顺序。
package template;
public abstract class CaffeineBeverage {
// 算法步骤由final确定
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");
}
}
这就是模板方法模式:定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现,将一些步骤延迟到子类中。利用模板方法模式,子类只需修改几个步骤,就可以重新定义算法。
但模板方法模式也存在一些缺陷。
首先,算法被封装在抽象类中,操作由子类实现,所以必须使用继承。
其次,如果把算法的每个步骤分得太大,可能导致代码的重复,如果分得太小,又会缺少弹性。
当一些步骤是可选实现时,如果某些子类不想修改,就需要反复编写相同的代码。针对这一问题,可以使用钩子。
钩子
所谓钩子就是在算法的步骤中对某些方法使用条件判断,将条件判断封装到可以重写的方法中,现在这个方法有默认的行为,而子类可以通过决定是否重写这个方法来决定是否执行其所影响的步骤。
package template;
public abstract class CaffeineBeverageWithHook {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
// customerWantsCondiments方法是一个钩子
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;
}
}
钩子是一个很形象的比喻,这种结构就像为某些额外的功能提供了挂载的空间,当需要使用这些功能时,就可以使用钩子将这些功能附加在基础的算法上。
设计原则
好莱坞原则:别调用我们(高层),我们会调用你(低层)。
此原则类似于依赖倒置原则,不过后者的关注点在于避免依赖,而此原则目的在于避免环状依赖(低层依赖高层,而高层依赖另一个低层导致低层依赖低层的现象)造成结构缺乏弹性的后果。

浙公网安备 33010602011771号