JAVA設(shè)計(jì)模式系列:
模板方法模式
定義
模板方法模式在一個(gè)方法中定義了算法的骨架,把其中的某些步驟延遲到子類(lèi)的實(shí)現(xiàn),是為我們提供了代碼復(fù)用的一種重要的技巧。模板方法使得子類(lèi)可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法中的某些步驟。
實(shí)現(xiàn)
這里簡(jiǎn)單通過(guò)一個(gè)示例來(lái)展示到底什么時(shí)候模板方法模式。這個(gè)示例向我們展示了制作咖啡和茶2種咖啡因飲料的過(guò)程,在這個(gè)過(guò)程中展示了模板方法模式的具體使用方法。
代碼地址:GitHub
先看一下模板方法模式的類(lèi)圖:
首先我們定義一個(gè)抽象類(lèi)
CaffeineBeverage來(lái)作為模板方法的基類(lèi)。具體代碼如下:
public abstract class CaffeineBeverage {
// 模板方法
final void prepareReipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
// 浸泡
abstract void brew();
// 加料
abstract void addCondiments();
// 煮水
void boilWater() {
System.out.println("Boiling water");
}
// 倒進(jìn)杯子里
void pourInCup() {
System.out.println("Pouring into cup");
}
在CaffeineBeverage類(lèi)中定義了一個(gè)名為prepareReipe()的模板方法,用來(lái)描述沖泡咖啡因飲料的過(guò)程。方法用final修飾是為了防止子類(lèi)修改方法的執(zhí)行順序。
CaffeineBeverage類(lèi)定義了4個(gè)方法,分別是brew()、addCondiments()、boilWater()、pourInCup()。在我們的示例中,沖泡咖啡和茶共有的過(guò)程分別是煮水 boilWater()、倒進(jìn)杯子里 pourInCup()。這兩個(gè)共用方法選擇在CaffeineBeverage類(lèi)實(shí)現(xiàn)。
Tea類(lèi)、Coffee類(lèi)是CaffeineBeverage類(lèi)的子類(lèi)。而加料 addCondiments()、浸泡 brew()分別在Tea類(lèi)、Coffee類(lèi)中有各自不同的實(shí)現(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");
}
}
完成了模板方法模式的代碼,我們可以進(jìn)行測(cè)試一下,測(cè)試類(lèi):
public class Test {
public static void main(String[] args) {
Tea tea = new Tea();
tea.prepareReipe();
System.out.println("**************");
Coffee coffee = new Coffee();
coffee.prepareReipe();
}
}
輸出結(jié)果:
Boiling water
Stepping the tea.
Pouring into cup
Adding Lemon
**************
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Suger and Mike
我們將沖茶和咖啡重復(fù)的方法煮水 boilWater()、倒進(jìn)杯子里 pourInCup()抽象出來(lái),每個(gè)子類(lèi)分別去實(shí)現(xiàn)各自特有的步驟。以上便是模板方法的實(shí)例。
鉤子
還需了解到,模板方法模式還有鉤子的概念。鉤子是一種被聲明在抽象類(lèi)的方法,可以為空或者默認(rèn)的實(shí)現(xiàn)。鉤子的存在可以讓子類(lèi)有能力對(duì)算法的不同點(diǎn)進(jìn)行掛鉤,是否需要掛鉤由子類(lèi)決定。
借助上面的示例來(lái)展示鉤子如何使用。首先我們?cè)诔橄箢?lèi)CaffeineBeverage定一個(gè)鉤子,鉤子的默認(rèn)實(shí)現(xiàn)返回true。如下:
// 定義一個(gè)鉤子
boolean customerWantsCondiments() {
return true;
}
并修改模板方法:
// 模板方法
final void prepareReipe() {
boilWater();
brew();
pourInCup();
if (customerWantsCondiments()) {
addCondiments();
}
}
目的是增加讓客戶(hù)選擇是否需要給茶或者飲料來(lái)添加?xùn)|西。我們可以在子類(lèi)中覆蓋鉤子的寫(xiě)法。這里改下下Tea類(lèi),如下:
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;
}
}
}
添加了一個(gè)msg變量,可以通過(guò)構(gòu)造函數(shù)進(jìn)行賦值,當(dāng)msg為y時(shí)候,我們將在茶里添加檸檬,否則不添加??匆幌聹y(cè)試代碼:
public static void main(String[] args) {
Tea tea = new Tea("n");
tea.prepareReipe();
System.out.println("**************");
Coffee coffee = new Coffee();
coffee.prepareReipe();
}
運(yùn)行結(jié)果:
Boiling water
Stepping the tea.
Pouring into cup
**************
Boiling water
Dripping Coffee through filter
Pouring into cup
Adding Suger and Mike
和上面的比較一下,發(fā)現(xiàn)制作茶的過(guò)程中缺少了添加?xùn)|西的過(guò)程,主要是因?yàn)槲覀冊(cè)?code>Tea類(lèi),重寫(xiě)了鉤子,來(lái)控制加料的步驟。
如有紕漏,煩請(qǐng)指出。
代碼地址:GitHub
參考《Head First 設(shè)計(jì)模式》