模板方法模式--封裝演算法

黑風S發表於2014-08-12

模板方法模式:在一個方法中定義一個演算法的步驟,而將一些步驟延遲到子類中。模板方法使得子類在不改變演算法結構的情況下,重新定義演算法的某些步驟。

下面用一個例子來詳細解釋一下。

有些人沒有咖啡就活不下去,有些人離不開茶,兩者共同的成分是什麼?當然是咖啡因了。但,還不只是這樣,茶和咖啡的沖泡方式非常相似,不信你瞧瞧:

星巴茲咖啡沖泡法:

(1) 把水煮沸

(2) 用沸水沖泡咖啡

(3) 把咖啡倒進杯子

(4) 加糖和牛奶

星巴茲茶沖泡法:

(1) 把水煮沸

(2) 用沸水浸泡茶葉

(3) 把茶倒進杯子

(4) 加檸檬


那用程式碼實現就是:

public class Coffee {
void prepareRecipe() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}

public void boilWater() {
System.out.println("Boiling water");
}

public void brewCoffeeGrinds() {
System.out.println("Dripping Coffee through filter");
}

public void pourInCup() {
System.out.println("Pouring into cup");
}

public void addSugarAndMilk() {
System.out.println("Adding Sugar and Milk");
}
}

接下來是茶

public class Tea {
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}

public void boilWater() {
System.out.println("Boiling water");
}

public void steepTeaBag() {
System.out.println("Steeping the tea");
}

public void addLemon() {
System.out.println("Add lemon");
}

public void pourInCup() {
System.out.println("Pouring into cup");
}
}


我們發現了重複的程式碼,這是好現象。這表示我們需要清理一下設計了。在這裡,既然茶和咖啡師如此的相似,似乎我們應該把共同的部分抽出來,放進一個基類裡。

看起來這個咖啡和茶的設計練習是相當的直接,你的第一版設計看起來是這樣:

CaffeineBeverage:

prepareRecipe();

boilWater();

pourInCup();


Coffee:

prepareRecipe();

brewCoffeeGrinds();

addSugarAndMilk();


Tea

prepareRecipe();

steepTeaBag();

addLemon();


我們的新設計你 覺得怎麼樣?是不是覺得忽略了某些其他共同點?咖啡和茶還有什麼是相似的?我們先從沖泡法入手

(1) 把水煮沸

(2) 用沸水沖泡咖啡

(3) 把咖啡倒進杯子

(4) 加糖和牛奶

(1)、(3)我們已經抽出來放到基類裡了,(2)、(4)並沒有被抽出來,但是他們是一樣的,只是應用在不同的飲料上。

那麼我們有辦法將prepareRecipe()也抽象化嗎?是的,現在就來看看該怎麼做,讓我們從每一個子類中逐步抽象prepareRecipe(),我們遇到的第一個問題,就是咖啡使用brewCoffeeGrinds()和addSugarAndMilk()方法,而茶使用的是steepTeaBag()和addLemon()方法。

讓我們來思考這一點,浸泡(steep)和沖泡(brew)差異其實不大,所以我們給他一個新的方法名稱,比方說brew(),然後不管泡茶還是沖泡咖啡我們都用這個名稱。類似的,加糖和牛奶也和加檸檬很相似,都是在飲料中加入調料,讓我們也用一個新的方法名稱來解決這個問題,就叫做addCondiments()好了。這樣一來,新的prepareRecipe()方法看起來就是這樣:

void prepareRecipe() {

boilWater();

brew();

pourInCup();

addCondiments();

}

現在我們有了新的prepareRecipe()方法,但是需要讓他能夠符合程式碼。要想這麼做,我們就先從CaffeineBeverage()超類開始:

public abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}

abstract void brew();
abstract void addCondiments();

public void boilWater() {
System.out.println("Boiling water");
}

public void pourInCup() {
System.out.println("Pouring into cup");
}
}

最後我們就需要處理咖啡和茶類了,這兩個類現在都是依賴超類來處理沖泡法,所以只需自行處理沖泡和新增調料部分:

public class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("Steeping the tea");
}

public void addCondiments() {
System.out.println("Adding Lemon");
}
}


public class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("Dripping coffee through filter");
}

public void addCondiments() {
System.out.println("Adding Sugar And Milk");
}
}

基本上,我們剛剛實現的就是模板方法模式。模板方法定義了一個演算法的步驟,並允許子類為一個或多個步驟提供實現。

從上面我們可以看出,模板方法模式的優勢就是:

1、可以將程式碼的複用最大化。

2、演算法只存在於一個地方,所以容易修改。

3、這個模板提供了一個框架,可以讓其他的咖啡因飲料插進來。

4、CaffeineBeverage類專注在演算法本身,而由子類提供完整的實現。


相關文章