定義
在一個方法中定義一個算法的骨架芭商,而將一些步驟延遲到子類中派草。模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法中的某些步驟铛楣。
生活事例引入
咖啡沖泡法
- 把水煮沸
- 用沸水沖泡咖啡
- 把咖啡倒進杯子
- 加糖和牛奶
茶沖泡法 - 把水煮沸
- 用沸水浸泡茶葉
- 把茶倒進杯子
- 加檸檬
可以發(fā)現(xiàn)上面兩種飲料的沖泡法在步驟上存在一樣的地方近迁,只是具體的動作不一樣,例如第四步都是沖泡的時候加一些佐料簸州,咖啡沖泡法是加的糖和牛奶鉴竭,茶沖泡法加的是檸檬
既然存在一些重復(fù)或者相似的地方,我們此時考慮將他們共同的部分抽取出來岸浑,放進一個基類中搏存。
下面進行代碼封裝
首先是抽取出來的抽象類
package templatepattern;
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boidWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
void boidWater() {
System.out.println("Boiling Water");
}
void pourInCup() {
System.out.println("Pouring into Cup");
}
}
然后是Tea類
package templatepattern;
public class Tea extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Steeping the tea");
}
@Override
void addCondiments() {
System.out.println("Adding Lemon");
}
}
然后是Coffee類
package templatepattern;
public class Coffee extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Dripping Coffee through filter");
}
@Override
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}
代碼解釋
- 上述代碼中,抽象類中的prepareRecipe()方法是final類型的助琐,之所以這么做是因為這個方法定義了一系列的步驟祭埂,而又不想這些步驟被子類所刪減,因此讓這個方法類型是final兵钮。
- 不管是coffee還是tea蛆橡,boidWater()和 pourInCup()方法的內(nèi)容都是一樣的,因此在父類中實現(xiàn)掘譬,但是具體添加的佐料不一樣泰演,泡的東西不一樣,因此這兩個方法在子類中具體實現(xiàn)葱轩。
模板方法定義了一個算法的步驟睦焕,并允許子類為一個或者多個步驟提供實現(xiàn)
進一步模板模式
使用鉤子
- 鉤子就是一個空的方法,該方法什么也不做靴拱,這個方法在父類中垃喊,子類視情況決定要不要覆蓋他們。
用途舉例
package templatepattern;
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boidWater();
brew();
pourInCup();
addCondiments();
hook();
}
abstract void brew();
abstract void addCondiments();
void boidWater() {
System.out.println("Boiling Water");
}
void pourInCup() {
System.out.println("Pouring into Cup");
}
void hook() {//hook就是一個鉤子袜炕,該方法什么也不做
}
}
- 鉤子實例
package templatepattern;
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boidWater();
brew();
pourInCup();
if (customerWantsCondiments()) {//此處加了條件語句
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boidWater() {
System.out.println("Boiling Water");
}
void pourInCup() {
System.out.println("Pouring into Cup");
}
boolean customerWantsCondiments() {//該方法就是一個鉤子本谜,子類可以覆蓋這個方法,但也不一定非得覆蓋
return true;
}
}
創(chuàng)建一個模板方法什么時候用鉤子偎窘,什么時候用抽象方法呢
當子類必須提供算法中某個方法或者步驟的實現(xiàn)時乌助,就使用抽象方法溜在,如果算法的這個部分是可選的,就用鉤子他托。如果選擇用鉤子的話掖肋,子類可以選擇實現(xiàn)這個鉤子,但并不強制這么做赏参。
好萊塢原則
別調(diào)用我們志笼,我們會調(diào)用你。
- 比如在模板設(shè)計模式中,我們告訴子類,不要到用我們河劝,我們會調(diào)用你。
Applet Swing 還有Comparable接口的compareTo()方法都采用了模板方法模式