JAVA設計模式系列:
模板方法模式
定義
模板方法模式在一個方法中定義了算法的骨架,把其中的某些步驟延遲到子類的實現(xiàn),是為我們提供了代碼復用的一種重要的技巧。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。
實現(xiàn)
這里簡單通過一個示例來展示到底什么時候模板方法模式。這個示例向我們展示了制作咖啡和茶2種咖啡因飲料的過程,在這個過程中展示了模板方法模式的具體使用方法。
代碼地址:GitHub
先看一下模板方法模式的類圖:

首先我們定義一個抽象類
CaffeineBeverage
來作為模板方法的基類。具體代碼如下:
public abstract class CaffeineBeverage {
// 模板方法
final void prepareReipe() {
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");
}
在CaffeineBeverage
類中定義了一個名為prepareReipe()
的模板方法,用來描述沖泡咖啡因飲料的過程。方法用final
修飾是為了防止子類修改方法的執(zhí)行順序。
CaffeineBeverage
類定義了4個方法,分別是brew()
、addCondiments()
、boilWater()
、pourInCup()
。在我們的示例中,沖泡咖啡和茶共有的過程分別是煮水 boilWater()
、倒進杯子里 pourInCup()
。這兩個共用方法選擇在CaffeineBeverage
類實現(xiàn)。
Tea
類、Coffee
類是CaffeineBeverage
類的子類。而加料 addCondiments()
、浸泡 brew()
分別在Tea
類、Coffee
類中有各自不同的實現(xiàn)。如下所示:
public class Tea extends CaffeineBeverage {
void brew() {
System.out.println("Stepping the tea.");
}
void addCondiments() {
System.out.println("Adding Lemon");
}
}
public class Coffee extends CaffeineBeverage {
void brew() {
System.out.println("Dripping Coffee through filter");
}
void addCondiments() {
System.out.println("Adding Suger and Mike");
}
}
完成了模板方法模式的代碼,我們可以進行測試一下,測試類:
public class Test {
public static void main(String[] args) {
Tea tea = new Tea();
tea.prepareReipe();
System.out.println("**************");
Coffee coffee = new Coffee();
coffee.prepareReipe();
}
}
輸出結果:
Boiling water
Stepping the tea.
Pouring into cup
Adding Lemon
**************
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Suger and Mike
我們將沖茶和咖啡重復的方法煮水 boilWater()
、倒進杯子里 pourInCup()
抽象出來,每個子類分別去實現(xiàn)各自特有的步驟。以上便是模板方法的實例。
鉤子
還需了解到,模板方法模式還有鉤子
的概念。鉤子
是一種被聲明在抽象類的方法,可以為空或者默認的實現(xiàn)。鉤子
的存在可以讓子類有能力對算法的不同點進行掛鉤,是否需要掛鉤由子類決定。
借助上面的示例來展示鉤子
如何使用。首先我們在抽象類CaffeineBeverage
定一個鉤子,鉤子的默認實現(xiàn)返回true
。如下:
// 定義一個鉤子
boolean customerWantsCondiments() {
return true;
}
并修改模板方法:
// 模板方法
final void prepareReipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
目的是增加讓客戶選擇是否需要給茶或者飲料來添加東西。我們可以在子類中覆蓋鉤子
的寫法。這里改下下Tea
類,如下:
public class Tea extends CaffeineBeverage {
private String msg;
public Tea(String msg) {
this.msg = msg;
}
void brew() {
System.out.println("Stepping the tea.");
}
void addCondiments() {
System.out.println("Adding Lemon");
}
boolean customerWantsCondiments() {
if ("y".equals(this.msg)) {
return true;
} else {
return false;
}
}
}
添加了一個msg
變量,可以通過構造函數(shù)進行賦值,當msg
為y
時候,我們將在茶里添加檸檬,否則不添加。看一下測試代碼:
public static void main(String[] args) {
Tea tea = new Tea("n");
tea.prepareReipe();
System.out.println("**************");
Coffee coffee = new Coffee();
coffee.prepareReipe();
}
運行結果:
Boiling water
Stepping the tea.
Pouring into cup
**************
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Suger and Mike
和上面的比較一下,發(fā)現(xiàn)制作茶的過程中缺少了添加東西的過程,主要是因為我們在Tea
類,重寫了鉤子,來控制加料的步驟。
如有紕漏,煩請指出。
代碼地址:GitHub
參考《Head First 設計模式》