關(guān)注公眾號 MageByte,設(shè)置星標(biāo)獲取最新干貨。公眾號后臺回復(fù) “加群” 進(jìn)入技術(shù)交流群獲更多技術(shù)成長。
——本文由 MageByte 團(tuán)隊的「青葉」編寫
模板方法模式在一個方法中定義了一個算法骨架,并且 final 修飾防止子類重寫。方法中包含一些抽象方法,也就是一些步驟延遲到字類實現(xiàn)。模板方法使得在不改變算法結(jié)構(gòu)的情況下,重新定義算法中的某些步驟。完整代碼可以查看GitHub:https://github.com/UniqueDong/zero-design-stu
類圖

模式實現(xiàn)
在實現(xiàn)模板方法模式時,開發(fā)抽象類的軟件設(shè)計師和開發(fā)具體子類的軟件設(shè)計師之間可以進(jìn)行協(xié)作。一個設(shè)計師負(fù)責(zé)給出一個算法的輪廓和框架,另一些設(shè)
計師則負(fù)責(zé)給出這個算法的各個邏輯步驟。實現(xiàn)這些具體邏輯步驟的方法即為基本方法,而將這些基本方法匯總起來的方法即為模板方法,模板方法模式的
名字也因此而來。下面將詳細(xì)介紹模板方法和基本方法:
- 模板方法
一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總算法或一個總行為的方法。這個模板方法定義在抽象類中,并由子類不加以修改
地完全繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中可以是具體方法,也可以是抽象方法。由于模板方法
是具體方法,因此模板方法模式中的抽象層只能是抽象類,而不是接口。 - 基本方法
基本方法是實現(xiàn)算法各個步驟的方法,是模板方法的組成部分?;痉椒ㄓ挚梢苑譃槿N:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。
(1) 抽象方法:一個抽象方法由抽象類聲明、由其具體子類實現(xiàn)。
(2) 具體方法:一個具體方法由一個抽象類或具體類聲明并實現(xiàn),其子類可以進(jìn)行覆蓋也可以直接繼承。
(3) 鉤子方法:一個鉤子方法由一個抽象類或具體類聲明并實現(xiàn),而其子類可能會加以擴(kuò)展。通常在父類中給出的實現(xiàn)是一個空實現(xiàn),并以該空實
現(xiàn)作為方法的默認(rèn)實現(xiàn),當(dāng)然鉤子方法也可以提供一個非空的默認(rèn)實現(xiàn)。
鉤子可以讓子類實現(xiàn)算法中可選的部分,或者在鉤子對于子類的實現(xiàn)并不重要的時候,子類可以對此鉤子置之不理。鉤子的另一個用法,是讓子類能夠有機(jī)會
對模板方法中某些即將發(fā)生的(或剛剛發(fā)生的)步驟做出反應(yīng)。
使用場景
開一家咖啡、茶館,泡茶和咖啡的沖泡方式非常相似:
星巴克咖啡沖泡法
- 把水煮沸
- 用沸水沖泡咖啡
- 把咖啡倒進(jìn)杯子
- 加糖和牛奶
功夫茶沖泡法
- 把水煮沸
- 用沸水沖泡茶葉
- 把茶倒進(jìn)杯子
- 加檸檬
我們可以發(fā)現(xiàn)兩種茶的步驟1,和步驟3是一樣的。整體算法結(jié)構(gòu)是固定的,只是有的部分不一樣。這時候我們就可以使用模板方法設(shè)計模式定義制作骨架,然后部分細(xì)節(jié)留給子類實現(xiàn)。
代碼實現(xiàn)
首先我們先抽象一個制作飲料的模板,定義算法邏輯 AbstractBeverage。同時有一個鉤子方法,一般是空實現(xiàn),在這里我們可以通過它(customerWantsCondiments())來控制是否加調(diào)料。
package com.zero.design.actions.template;
/**
* 抽象制作飲料模板:定義算法骨架
*/
public abstract class AbstractBeverage {
/**
* 這就是模板方法。它被聲明為final,以免子類改變這個算法的順序。
* 算法步驟組合
*/
final void prepareRecipe() {
// 模板方法定義了一連串的步驟,每個步驟由一個方法代表
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
/**
* 我們在這里定義了一個方法,(通常)是空的缺省實現(xiàn)。這個方法只會返回true,不做別的事。
* 這就是一個鉤子,子類可以覆蓋這個方法,但不見得一定要這么。
* @return
*/
boolean customerWantsCondiments() {
return true;
}
/**
* 添加佐料:不同飲料也有不同佐料:申明為抽象類,由子類取操心
*/
protected abstract void addCondiments();
/**
* 釀制:不同飲料方式也不同,申明為抽象類,由子類取操心
*/
protected abstract void brew();
/**
* 共通方法:倒入杯中
*/
public void pourInCup() {
System.out.println("倒入杯子中...");
}
/**
* 把水煮沸,共通方法
*/
public void boilWater() {
System.out.println("把水煮沸...");
}
}
接著我們定義泡茶具體算法,并且繼承 AbstractBeverage 抽象算法,實現(xiàn)泡茶的具體邏輯。
package com.zero.design.actions.template;
public class Tea extends AbstractBeverage {
/**
* 這樣通過鉤子就可以選擇是都要加佐料了
*/
private boolean addCondiments = true;
/**
* 添加糖、牛奶
*/
@Override
protected void addCondiments() {
System.out.println("添加檸檬,茶更好喝");
}
/**
* 咖啡沖泡方法
*/
@Override
protected void brew() {
System.out.println("秘制泡茶方式放入茶葉");
}
/**
* 使用鉤子,不加佐料
* @return
*/
@Override
boolean customerWantsCondiments() {
return addCondiments;
}
public boolean isAddCondiments() {
return addCondiments;
}
public void setAddCondiments(boolean addCondiments) {
this.addCondiments = addCondiments;
}
}
定義咖啡的算法細(xì)節(jié)
package com.zero.design.actions.template;
/**
* 咖啡具體實現(xiàn):只需要自行處理沖泡和添加調(diào)料部分
*/
public class Coffe extends AbstractBeverage {
/**
* 這樣通過鉤子就可以選擇是都要加佐料了
*/
public boolean addCondiments = true;
/**
* 添加糖、牛奶
*/
@Override
protected void addCondiments() {
System.out.println("添加糖跟牛奶");
}
/**
* 咖啡沖泡方法
*/
@Override
protected void brew() {
System.out.println("放入咖啡豆,使用秘制方法沖泡");
}
/**
* 重寫鉤子
* @return
*/
@Override
boolean customerWantsCondiments() {
return addCondiments;
}
public boolean isAddCondiments() {
return addCondiments;
}
public void setAddCondiments(boolean addCondiments) {
this.addCondiments = addCondiments;
}
}
接著就是客戶點單,我們通過模板方法模式制作咖啡或者功夫茶。達(dá)到代碼復(fù)用。
package com.zero.design.actions.template;
/**
* Created by unique on 2017/6/7.
*/
public class Test {
public static void main(String[] args) {
Tea tea = new Tea();
tea.setAddCondiments(false);
tea.prepareRecipe();
System.out.println("-------------------");
Coffe coffe = new Coffe();
coffe.prepareRecipe();
}
}
輸出如下
把水煮沸...
秘制泡茶方式放入茶葉
倒入杯子中...
-------------------
把水煮沸...
放入咖啡豆,使用秘制方法沖泡
倒入杯子中...
添加糖跟牛奶
歡迎加群與我們分享,我們第一時間反饋。關(guān)注公眾號,后臺回復(fù) 加群即可獲取個人微信。
