模板方法模式定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
以一个冲咖啡和冲茶的实例来说明,它们都有类似的步骤:煮沸水、冲咖啡(茶)、倒进杯子、加调料。
下面是咖啡的代码:
public class Coffee {
void PrepareRecipe(){
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugerAndMilk();
}
public void addSugerAndMilk() {
System.out.println("Adding Suger and Milk");
}
public void pourInCup() {
System.out.println("Pouring into Cup");
}
public void brewCoffeeGrinds() {
System.out.println("Dripping coffee through filter");
}
public void boilWater() {
System.out.println("Boiling Water");
}
}
茶的代码:
public class Tea {
void PrepareRecipe(){
boilWater();
steepTeaBag(); //这是泡茶专有的方法
pourInCup();
addLemon(); //这是泡茶专有的方法
}
public void addLemon() {
System.out.println("Adding lemon");
}
public void steepTeaBag() {
System.out.println("Steeping the tea");
}
public void pourInCup() {
System.out.println("Pouring into Cup");
}
public void boilWater() {
System.out.println("Boiling Water");
}
}
这两个类中有许多相同的代码,我们可以将其抽离,新建一个抽象类CaffeineBeverage:
public abstract class CaffeineBeverage {
final void perpareRecipe(){ //我们不希望子类覆盖这个方法。
boilWater();
brew();
pourInCup();
addCondiments();
}
protected abstract void addCondiments();
private void pourInCup() {
System.out.println("Pouring in Cup");
}
protected abstract void brew();
private void boilWater() {
System.out.println("Boiling Water");
}
}
于是上面的两个coffee与tea类可以改成:
public class Tea extends CaffeineBeverage{
@Override
protected void addCondiments() {
System.out.println("Adding Lemon");
}
@Override
protected void brew() {
System.out.println("Steeping the Tea");
}
}
public class Coffee extends CaffeineBeverage {
@Override
protected void addCondiments() {
System.out.println("adding suger and milk");
}
@Override
protected void brew() {
System.out.println("dripping coffee through filter");
}
}
我们做的工作:将冲泡的方法放到基类中,它是一个算法的模板。某些方法是由这个类处理的(第一个与第三个),某些方法是由子类实现处理的(第二个与第四个)。
这样做的好处:
由CaffeineBeverage类主导一切,它拥有算法,而且保护这个算法。
对子类来说,CaffeineBeverage的存在,可以将代码的复用最大化。
算法只存在于一个地方,所以很容易修改(一开始存在于两个文件中,如果修改要修改两处)。
如果新的咖啡因饮料加进来,只需要实现自己独有的方法就可以了。
CaffeineBeverage类专注在算法本身,而由子类提供完整的实现。
模板方法模式的具体定义:模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
这就是模板方法:
final void perpareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
它被声明为final,以免子类改变这个算法的顺序。
钩子:
我们可以有”默认不做事的方法”,我们称这种方法为hook。子类可以视情况决定要不要覆盖它们。final void perpareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
hook();
}
private void hook() {
}
钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类来决定。钩子可以让子类有能力为其抽象类作一些决定。