模板方法模式是學習的第二個模式,理解容易卻極具實用價值。
定義演算法的骨架,將某些步驟延遲到子類別實作,讓子類別在不改變演算法整體架構下,重新定義特定步驟。
原有一功能完整的類別,因需求變更,新類別與舊類別 60% 相同,僅 40% 不同。希望保留共同部分,僅改寫差異處。
例如:原生產「自動泡茶機」,現欲新增「自動泡咖啡機」,兩者架構與流程相似:
| 自動泡茶機 | 自動泡咖啡機 |
|---|---|
| step1: 煮開水 | step1: 煮開水 |
| step2: 放入茶葉 | step2: 放入咖啡粉 |
| step3: 倒入杯子 | step3: 倒入杯子 |
| step4: 加檸檬 | step4: 加糖與奶精 |
step1 相同,step2~4 不同。設計 DrinkMachine 基類定義流程與 step1,step2~4 宣告為虛擬函數留給子類別實作,即為模板方法模式。
此架構符合開放封閉原則 (OCP):新增飲料無需修改既有類別,僅需繼承並實作差異步驟。
模板方法模式就是將不同的成員函數用類別包裝,由衍生類別覆寫。
/**/*
(C) OOMusou 2007 https://oomusou.cnblogs.com
Filename : DP_TemplateMethodPattern1.cpp
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
Description : Demo how to use Template Method Pattern
Release : 03/31/2007 1.0
*/
#include <iostream>
using namespace std;
class DrinkMachine {
public:
void makeDrink();
protected:
void boilWater() { cout << "boil some water" << endl; }
virtual void doPutIngredient() const = 0;
virtual void doPourInCup() const = 0;
virtual void doAddFlavoring() const = 0;
};
void DrinkMachine::makeDrink() {
this->boilWater();
this->doPutIngredient();
this->doPourInCup();
this->doAddFlavoring();
}
class TeaMachine : public DrinkMachine {
protected:
void doPutIngredient() const { cout << "steep tea in boiling water" << endl; }
void doPourInCup() const { cout << "pour tea in cup" << endl; }
void doAddFlavoring() const { cout << "add lemon" << endl; }
};
class CoffeeMachine : public DrinkMachine {
protected:
void doPutIngredient() const { cout << "brew coffee in boiling water" << endl; }
void doPourInCup() const { cout << "pour coffee in cup" << endl; }
void doAddFlavoring() const { cout << "add sugar and milk." << endl; }
};
int main() {
cout << "Making Tea" << endl;
DrinkMachine *theDrinkMachine = &TeaMachine();
theDrinkMachine->makeDrink();
cout << endl;
cout << "Making Coffee" << endl;
theDrinkMachine = &CoffeeMachine();
theDrinkMachine->makeDrink();
}
執行結果:
Making Tea
boil some water
steep tea in boiling water
pour tea in cup
add lemon
Making Coffee
boil some water
brew coffee in boiling water
pour coffee in cup
add sugar and milk.
#include <iostream>
using namespace std;
template<typename T>
class DrinkMachine {
public:
void makeDrink() {
T* derived = (T*) this;
this->boilWater();
derived->doPutIngredient();
derived->doPourInCup();
derived->doAddFlavoring();
}
protected:
void boilWater() {
cout << "boil some water" << endl;
}
};
class TeaMachine : public DrinkMachine<TeaMachine> {
friend DrinkMachine<TeaMachine>;
protected:
void doPutIngredient() const { cout << "steep tea in boiling water" << endl; }
void doPourInCup() const { cout << "pour tea in cup" << endl; }
void doAddFlavoring() const { cout << "add lemon" << endl; }
};
class CoffeeMachine : public DrinkMachine<CoffeeMachine> {
friend DrinkMachine<CoffeeMachine>;
protected:
void doPutIngredient() const { cout << "brew coffee in boiling water" << endl; }
void doPourInCup() const { cout << "pour coffee in cup" << endl; }
void doAddFlavoring() const { cout << "add sugar and milk." << endl; }
};
int main() {
cout << "Making Tea" << endl;
DrinkMachine<TeaMachine> *pTeaMachine = &TeaMachine();
pTeaMachine->makeDrink();
cout << endl;
cout << "Making Coffee" << endl;
DrinkMachine<CoffeeMachine> *pCoffeeMachine = &CoffeeMachine();
pCoffeeMachine->makeDrink();
}
兩者目的相同:為新需求的不同演算法提供擴充機制。但手法不同:
GoF 原文:
Template methods use inheritance to vary part of an algorithm. Strategies use delegation to vary the entire algorithm.