太久沒寫c++了 來找一下手感
所以這次是c++的範例喔~
這是書中的範例喔,假設今天我們要來寫製作飲品的食譜,分別有茶跟飲料兩種類別 (先省略方法細節):
class Tea{
public:
void prepareRecipe();
void boilWater();
void steepTeaBag();
void pourInCup();
void addLemon();
};
void Tea::prepareRecipe(){
cout << "Recipe:" << endl;
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
class Coffee{
public:
void prepareRecipe();
void boilWater();
void brewCoffeeGrinds();
void pourInCup();
void addSugarAndMilk();
};
void Coffee::prepareRecipe(){
cout << "Recipe:" << endl;
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
我們可以很快地發現 boilWater()
pourInCup()
是相同的方法,
經過了前面章節的淬煉,這種事是絕對不允許的!
於是可以簡化後改寫:
class Beverage{
public:
void prepareRecipe();
void boilWater();
void pourInCup();
public:
virtual void brewIngredients() = 0;
virtual void addCondiment() = 0;
};
增加一個父類別 Beverage
將重複的方法寫進去,另外 steepTeaBag()
, brewCoffeeGrinds()
都是在煮主要材料的過程,所以可以先簡化成brewIngredients()
的抽象方法。而addLemon()
, addSugarAndMilk()
也是,都屬於增加調味的方法,那就改寫成 addCondiment()
。這兩個抽象方法就留給繼承後的子類別分別去定義實作。
class Coffee: public Beverage{
public:
void brewIngredients();
void addCondiment();
};
void Coffee::brewIngredients(){
cout << "Brew the coffee grinds" << endl;
}
void Coffee::addCondiment(){
cout << "Add sugar and some milk" << endl;
}
class Tea: public Beverage{
public:
void brewIngredients();
void addCondiment();
};
void Tea::brewIngredients() {
cout << "Steep the tea bag" << endl;
}
void Tea::addCondiment() {
cout << "Add sugar and some milk" << endl;
}
void Beverage::prepareRecipe(){
cout << "Recipe:" << endl;
boilWater();
brewIngredients();
pourInCup();
addCondiment();
}
這就是模板模式的精髓,將程式打造成一個大致的萬用架構(父類別 Beverage
),讓子類別(Coffee
, Tea
)按照架構自由覆寫方法實作內容,如果需要調整方法,都可以直接從父類別的方法下手,避免重複程式碼帶來的維護問題。
class Beverage{
public:
void prepareRecipe();
void boilWater();
void pourInCup();
public:
virtual void brewIngredients() = 0;
virtual void addCondiment() = 0;
virtual bool customerNeedsCondiment() = 0;
};
void Beverage::prepareRecipe(){
cout << "Recipe:" << endl;
boilWater();
brewIngredients();
pourInCup();
if(customerNeedsCondiment()){
addCondiment();
}
}
模板方法另一個特性是可以使用hook來讓程式更佳彈性,讓子類別自由選擇是否要去實踐某個方法。例如在上述範例Beverage
加上customerNeedsCondiment()
,如果客人需要調味料再去執行addCondiment()
。
若是出現新的食譜是不能加調味料的,也可以選擇不去使用:
class MilkTea: public Beverage{
public:
void brewIngredients();
void addCondiment();
bool customerNeedsCondiment();
};
void MilkTea::brewIngredients(){
cout << "Blend the tea and milk" << endl;
}
void MilkTea::addCondiment(){}
//預設不提供加調味料的選項
bool MilkTea::customerNeedsCondiment(){
return false;
}
來看一下實際運行狀況:
int main(){
Coffee latte;
latte.prepareRecipe();
cout << endl;
Tea signatureTea;
signatureTea.prepareRecipe();
MilkTea milkTea;
milkTea.prepareRecipe();
}
輸出:
Recipe:
Boil the water
Brew the coffee grinds
Pour it into the cup
Would you like some milk or sugar? (y/n)
y
Add sugar and some milk
Recipe:
Boil the water
Steep the tea bag
Pour it into the cup
Would you like some lemon? (y/n)
n
Recipe:
Boil the water
Blend the tea and milk
Pour it into the cup
Disclaimer
因為讀的是原文版,所以難免會有翻譯詞不達意或是專有名詞上的差異,有錯誤的話歡迎在留言區一起交流!