模板方法 模式
定义: 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
一个例子
假设有咖啡和茶两个类:
public class Coffee {
void prepareRecipe() {
// 1. 烧水
boilWater();
// 2. 用沸水冲泡咖啡
brewCoffeeGrinds();
// 3. 把咖啡倒进杯子
pourInCup();
// 4. 加糖和牛奶
addSugarAndMilk();
}
private void addSugarAndMilk() {
System.err.println("加柠檬");
}
private void pourInCup() {
System.err.println("茶倒入杯子里");
}
private void brewCoffeeGrinds() {
System.err.println("冲泡咖啡");
}
private void boilWater() {
System.err.println("将水煮沸");
}
}
package com.gitlearning.hanldegit.patterns.template.before;
public class Tea {
/**
* 沏茶的整个过程
*/
void prepareRecipe() {
// 1. 烧水
boilWater();
// 2. 浸泡茶叶(不是越狱中的T-bag哦)
steepTeaBag();
// 3. 把茶倒入杯子
pourInCup();
// 4. 加柠檬
addLemon();
}
private void addLemon() {
System.err.println("加柠檬");
}
private void pourInCup() {
System.err.println("茶倒入杯子里");
}
private void steepTeaBag() {
System.err.println("浸泡茶叶");
}
private void boilWater() {
System.err.println("将水煮沸");
}
}
我们发现沏茶和冲咖啡的过程有些是一样的,有些不一样。
prepareRecipe作为模板方法,抽取一个抽象类:
package com.gitlearning.hanldegit.patterns.template.after;
// 咖啡因饮料
public abstract class CaffeineBeverage {
void prepareRecipe() {
// 1. 烧水
boilWater();
// 2. 冲泡
brew();
// 3. 倒进杯子
pourInCup();
// 4. 加料
addCondiments();
}
protected abstract void addCondiments() ;
private void pourInCup() {
System.err.println("茶倒入杯子里");
}
protected abstract void brew() ;
private void boilWater() {
System.err.println("将水煮沸");
}
}
Caffee和Tea继承该抽象类,并实现各自的brew和addCondiments方法。
package com.gitlearning.hanldegit.patterns.template.after;
public class Coffee extends CaffeineBeverage {
@Override
protected void addCondiments() {
System.err.println("加奶和糖");
}
@Override
protected void brew() {
System.err.println("冲泡咖啡");
}
}
package com.gitlearning.hanldegit.patterns.template.after;
public class Tea extends CaffeineBeverage {
@Override
protected void addCondiments() {
System.err.println("加柠檬");
}
@Override
protected void brew() {
System.err.println("浸泡茶叶");
}
}
至此,使用模板方法模式解决两个类的冗余代码已经解决。
模板方法的使用
Java中使用到模板方法的地方:
AQS:acquire,release,acquireShared,releaseShared,里面调用子类方法tryAcquire,tryRelease,tryAcquireShared,tryReleaseShared。
InputStream:public int read(byte b[], int off, int len) throws IOException调用了read()方法,read方法由子类实现
其他
-
在github上的patterns.template包下面另外写了个参照AQS的写法,里面新增了beforeExecute()方法和afterExecute()钩子方法,钩子方法可以用来做一些日志记录或者性能测试相关的一些操作。
-
模板方法经常用在框架里,超类给定方法,具体实现由子类完成。
-
模板方法定义了算法步骤,并将实现延迟到子类
-
模板方法提供了一种代码复用的重要技巧
-
模板方法抽象类可以定义具体方法/抽象方法/钩子方法,钩子可以不做事,或default,子类可以选择是否覆写
-
模板方法模式和策略模式都封装算法,一个用组合,一个用继承
-
好莱坞原则:别调用我们,我们会调用你。模板方法的抽象类来调用子类的方法实现。
-
工厂方法是模板方法的一种特殊版本。