設(shè)計模式之模板方法
1.意圖
根據(jù)GOF中的定義:定義一個操作中的算法的骨架刹泄,而將一些步驟延遲到子類中實現(xiàn)脏答。模板方法使得子類可以不改變一個算法的結(jié)構(gòu)就可重定義算法的某些特定步驟躲撰。
很明顯,模板方法就是抽象出某個問題的解決算法,封裝算法框架嗅虏,開放一些特定步驟讓子類繼承抽象類后重新定義扎阶,從而實現(xiàn)改變這個解決算法事富。這在實際中是實用很廣的算法。
2.動機(jī)
這樣做有很大的好處乘陪,最重要的一點(diǎn)我認(rèn)為是根據(jù)某個問題的解決算法而定制化算法的特定步驟统台,做到使算法或操作框架穩(wěn)定,而實現(xiàn)步驟特定化啡邑,提高代碼的復(fù)用性贱勃。
模板方法通過一些抽象的操作定義一個算法,子類重定義這些操作谤逼,模板方法確定這些操作的順序贵扰,實現(xiàn)滿足各自的需求。
3.模式結(jié)構(gòu)與角色定義
如圖流部,抽象類定義某些特定步驟戚绕,然后templateMethod對這些操作確定順序(需要的話可以添加課外的邏輯代碼),具體類繼承抽象類枝冀,重定義這些特定的步驟舞丛。有時候耘子,在這個結(jié)構(gòu)中還有一個鉤子函數(shù),是用于控制模板方法的某些邏輯球切,一般是把鉤子函數(shù)放在抽象類中谷誓,然后具體類通過重寫鉤子函數(shù)來影響模板方法的邏輯,這樣做的好處是以防多個不同子類需要重新定義鉤子函數(shù)吨凑。這樣一個完整的模板方法就完成了捍歪。
在這個模板方法設(shè)計模式中,一共有以下的角色:
1.AbstractClass:
1.定義抽象的操作鸵钝,被子類繼承重寫以實現(xiàn)一個算法的步驟糙臼。
2.定義好一個算法的骨架或特定步驟。
2.ConcreteClass:
1.繼承抽象類恩商,重寫各個步驟弓摘,以實現(xiàn)完善算法的步驟
4.應(yīng)用場景
1.一次性實現(xiàn)算法的不變部分,并將可變的行為留給子類實現(xiàn)痕届。
2.各子類將公共的部分提取出來抽象父類韧献,然后各自繼承重寫特定操作,提高代碼的復(fù)用性
3.控制拓展子類研叫。通過鉤子函數(shù)锤窑,子類也可以控制算法的某些步驟邏輯。
5.模式的實現(xiàn)步驟與例子
模板的實現(xiàn)可以簡單總結(jié)為:
1.提取出不變或者公共的架構(gòu)嚷炉,把變化的抽象化渊啰,抽象成抽象類或接口。
2.具體類繼承抽象類或接口申屹,重寫變化的步驟
3.客戶端實例化具體類绘证,直接調(diào)用模板方法解決問題。
例子:去3天馬爾代夫旅游哗讥,定好目的地嚷那,并且小情侶想每天嘗試不同的交通方式,因此每天的交通工具不一樣杆煞,但是目的地都是一樣的魏宽。因此可以說是總體的旅游路線是一致的,但是每天的交通方法不一樣决乎,所以可以定好一個去目的地的模板队询,然后只改變3天的交通方式,就可以了构诚。
//抽象類
#include <iostream>
/************************************************************************/
/* 定義旅游交通方式的接口 */
/************************************************************************/
class ITravel {
public:
ITravel(){
is_other = false; //默認(rèn)不坐其他交通方式
}
virtual ~ITravel(){}
protected:
virtual bool Day1() = 0;
virtual void Day2() = 0;
virtual void Day3() = 0;
public:
//旅游路線模板
void Travel() {
std::cout<<"============旅行開始==========="<<std::endl;
if (Day1())
{
Day2();
}
if (is_other)
{
std::cout<<"額外方式:坐火箭"<<std::endl;
}
Day3();
std::cout<<"============旅行結(jié)束==========="<<std::endl;
std::cout<<std::endl;
}
//鉤子函數(shù)
virtual void SetAttrHood() = 0;
void SetOther(bool other) {
is_other = other;
}
private:
bool is_other;//是否乘坐其他
};
以上抽象類抽象出算法的框架蚌斩,并且定義好步驟。
//具體類A
class TravelA : public ITravel
{
public:
TravelA(){}
~TravelA(){}
protected:
bool Day1() override {
std::cout<<"第一天:坐大巴"<<std::endl;
return true;
}
void Day2() override {
std::cout<<"第一天:坐飛機(jī)"<<std::endl;
}
void Day3() override {
std::cout<<"第一天:坐輪船"<<std::endl;
}
void SetAttrHood() override {
SetOther(true);
}
};
具體類繼承抽象類范嘱,并且重寫特定的步驟送膳,定制化算法员魏。
//具體類B
class TravelB : public ITravel
{
public:
TravelB(){}
~TravelB(){}
protected:
bool Day1() override {
std::cout<<"步驟一:坐飛機(jī)"<<std::endl;
return true;
}
void Day2() override {
std::cout<<"步驟二:坐輪船,"<<std::endl;
}
void Day3() override {
std::cout<<"步驟三:坐大巴"<<std::endl;
}
void SetAttrHood() override {
SetOther(false);
}
};
具體類繼承抽象類肠缨,并且重寫特定的步驟逆趋,定制化算法盏阶。
//客戶端程序
#include "template_method.h"
int main()
{
ITravel* travelA = new TravelA();
travelA->SetAttrHood();
travelA->Travel();
ITravel* travelB = new TravelB();
travelB->SetAttrHood();
travelB->Travel();
system("pause");
delete travelA;
delete travelB;
return 0;
}
以下是打印結(jié)果:
============旅行開始===========
第一天:坐大巴
第一天:坐飛機(jī)
額外方式:坐火箭
第一天:坐輪船
============旅行結(jié)束===========
============旅行開始===========
步驟一:坐飛機(jī)
步驟二:坐輪船晒奕,
步驟三:坐大巴
============旅行結(jié)束===========
6.小結(jié)
模板方法是一個很常用的方法,能夠提高代碼的復(fù)用性名斟,關(guān)鍵點(diǎn)是找出解決一個問題的變與不變的分層點(diǎn)脑慧,封裝不變的,定義好骨架砰盐,留出變化的給子類重寫實現(xiàn)闷袒,就可以使同一個算法框架,被實現(xiàn)成解決相似的問題岩梳。