C++设计模式之模板模式(template)(行为型)
一 定義
模板方法模式是一種類的行為型模式,在它的結(jié)構(gòu)圖中只有類之間的繼承關(guān)系,沒有對象關(guān)聯(lián)關(guān)系,模板方法模式(Template Method Pattern)官方定義:定義一個操作中的算法的框架,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。
抽象模板中包含三種類型的方法:?基本方法、模板方法和鉤子方法(Hook Method)。
基本方法——基本方法也叫做基本操作,是由子類實現(xiàn)的方法,并且在模板方法被調(diào)用。
模板方法——核心方法,不允許子類重寫,所以都會加上final修飾符,可以有一個或幾個,一般是一個具體方法框架,按照固定的流程對基本方法的調(diào)度
鉤子方法——為了讓模板方法的執(zhí)行結(jié)果的更好地適應(yīng)因外界條件改變。比如說銀行辦理業(yè)務(wù)為例,辦理業(yè)務(wù)是個模板方法,普通人要經(jīng)歷排隊、取號、等待、辦理四個基本流程,而Vip則不需要排隊、取號、等待,那么在設(shè)計的時候我們的抽象模板類需要考慮到,于是乎你得需要在模板方法各基本方法調(diào)用之前增加條件判斷,那么用于設(shè)置這個條件的方法就是鉤子方法(Hook Method),鉤子方法也可以是抽象的還可以由子類的一個方法返回值決定公共部分的執(zhí)行結(jié)果。
二 ULM圖
角色:
(1)AbstractClass:是抽象類,其實也就是一個抽象模板,定義并實現(xiàn)了一個模板方法。這個模板方法一般是一個具體方法,它給出了一個頂級邏輯的框架,而邏輯的組成步驟在相應(yīng)的抽象操作中,推遲到子類實現(xiàn)。頂級邏輯也有可能調(diào)用一些具體方法。
(2)ConcreteClass:實現(xiàn)父類所定義的一個或多個抽象方法。每一個AbstractClass都可以有任意多個ConcreteClass與之對應(yīng),而每一個ConcreteClass都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現(xiàn),從而使得頂級邏輯的實現(xiàn)各不相同。
總結(jié):當不變的和可變的行為在方法的子類實現(xiàn)中混合在一起的時候,不變的行為就會在子類中重復(fù)出現(xiàn)。我們通過模板方法模式,把這些行為搬移到單一的地方,這樣幫助子類擺脫重復(fù)的不變行為的糾纏。
模板方法模式的優(yōu)點
良好的擴展性,封裝不變部分,擴展可變部分,把認為是不變部分的算法封裝到父類實現(xiàn),而可變部分的則可以通過繼承來繼續(xù)擴展。例如增加一個新的功能很簡單,只要再增加一個子類,實現(xiàn)父類的基本方法就可以了。
提取公共部分代碼,便于維護,減小維護升級成本,基本操作由父類定義,子類實現(xiàn)
基本方法是由子類實現(xiàn)的,因此子類可以通過擴展的方式增加相應(yīng)的功能,符合開閉原 則。
模板方法模式的缺點
通常抽象類是負責聲明某一類的事物的共同屬性和抽象方法,實現(xiàn)類則是完成定義具體的特性和方法。但是模板方法模式卻顛倒了,抽象類定義了部分抽象方法,由子類實現(xiàn),子類執(zhí)行的結(jié)果影響了父類的結(jié)果,也就是子類對父類產(chǎn)生了影響。每個不同的實現(xiàn)都需要定義一個子類,這會導(dǎo)致類的個數(shù)增加,系統(tǒng)更加龐大,設(shè)計也更加抽象,但是更加符合“單一職責原則”,使得類的內(nèi)聚性得以提高。
適合使用模板方法模式的場景
?四 實例
1. 填寫簡歷表
最近有個招聘會,可以帶上簡歷去應(yīng)聘了。但是,其中有一家公司不接受簡歷,而是給應(yīng)聘者發(fā)了一張簡歷表,上面有基本信息、教育背景、工作經(jīng)歷等欄,讓應(yīng)聘者按照要求填寫完整。每個人拿到這份表格后,就開始填寫。如果用程序?qū)崿F(xiàn)這個過程,該如何做呢?一種方案就是用模板方法模式:定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。我們的例子中,操作就是填寫簡歷這一過程,我們可以在父類中定義操作的算法骨架,而具體的實現(xiàn)由子類完成。下面給出它的UML圖。
?
#include <iostream> #include <memory>//簡歷 class Resume { protected: //保護成員virtual void SetPersonalInfo() {}virtual void SetEducation() {}virtual void SetWorkExp() {} public:void FillResume(){SetPersonalInfo();SetEducation();SetWorkExp();} };class ResumeA: public Resume { protected:void SetPersonalInfo(){std::cout << "A's PersonalInfo" << std::endl;}void SetEducation(){std::cout << "A's Education" << std::endl;}void SetWorkExp(){std::cout << "A's Work Experience" << std::endl;} };class ResumeB: public Resume { protected:void SetPersonalInfo(){std::cout << "B's PersonalInfo" << std::endl;}void SetEducation(){std::cout << "B's Education" << std::endl;}void SetWorkExp(){std::cout << "B's Work Experience" << std::endl;} };int main() {auto r1 = std::make_unique<ResumeA>();r1->FillResume();auto r2 = std::make_unique<ResumeB>();r2->FillResume();return 0; }?
五?模式的擴展
1)模板方法模式與控制反轉(zhuǎn)(好萊塢原則)
在模板方法模式中,子類不顯式調(diào)用父類的方法,而是通過覆蓋父類的方法來實現(xiàn)某些具體的業(yè)務(wù)邏輯,父類控制對子類的調(diào)用,這種機制被稱為好萊塢原則(Hollywood Principle),好萊塢原則的定義為:“不要給我們打電話,我們會給你打電話(Don‘t call us, we’ll call you)”。在好萊塢,把簡歷遞交給演藝公司后就只有回家等待。由演藝公司對整個娛樂項的完全控制,演員只能被動式的接受公司的差使,在需要的環(huán)節(jié)中,完成自己的演出。模板方法模式充分的體現(xiàn)了“好萊塢”原則。由父類完全控制著子類的邏輯,子類不需要調(diào)用父類,而通過父類來調(diào)用子類,子類可以實現(xiàn)父類的可變部份,卻繼承父類的邏輯,不能改變業(yè)務(wù)邏輯。
2)模板方法模式符合開閉原則
模板方法模式意圖是由抽象父類控制頂級邏輯,并把基本操作的實現(xiàn)推遲到子類去實現(xiàn),這是通過繼承的手段來達到對象的復(fù)用,同時也遵守了開閉原則。
父類通過頂級邏輯,它通過定義并提供一個具體方法來實現(xiàn),我們也稱之為模板方法。通常這個模板方法才是外部對象最關(guān)心的方法。在上面的銀行業(yè)務(wù)處理例子中,templateMethodProcess這個方法才是外部對象最關(guān)心的方法。所以它必須是public的,才能被外部對象所調(diào)用。
子類需要繼承父類去擴展父類的基本方法,但是它也可以覆寫父類的方法。如果子類去覆寫了父類的模板方法,從而改變了父類控制的頂級邏輯,這違反了“開閉原則”。我們在使用模板方法模式時,應(yīng)該總是保證子類有正確的邏輯。所以模板方法應(yīng)該定義為final的。所以AbstractClass類的模板方法templateMethodProcess方法應(yīng)該定義為final。
模板方法模式中,抽象類的模板方法應(yīng)該聲明為final的。因為子類不能覆寫一個被定義為final的方法。從而保證了子類的邏輯永遠由父類所控制。
3)模板方法模式與對象的封裝性
面向?qū)ο蟮娜筇匦?#xff1a;繼承,封裝,多態(tài)。
對象有內(nèi)部狀態(tài)和外部的行為。封裝是為了信息隱藏,通過封裝來維護對象內(nèi)部數(shù)據(jù)的完整性。使得外部對象不能夠直接訪問一個對象的內(nèi)部狀態(tài),而必須通過恰當?shù)姆椒ú拍茉L問。
對象屬性和方法賦予指定的修改符(public、protected、private)來達到封裝的目的,使得數(shù)據(jù)不被外部對象惡意的訪問及方法不被錯誤調(diào)用導(dǎo)造成破壞對象的封裝性。
降低方法的訪問級別,也就是最大化的降低方法的可見度是一種很重要的封裝手段。最大化降低方法的可見度除了可以達到信息隱藏外,還能有效的降低類之間的耦合度,降低一個類的復(fù)雜度。還可以減少開發(fā)人員發(fā)生的的錯誤調(diào)用。
一個類應(yīng)該只公開外部需要調(diào)用的方法。而所有為public方法服務(wù)的方法都應(yīng)該聲明為protected或private。如是一個方法不是需要對外公開的,但是它需要被子類進行擴展的或調(diào)用。那么把它定義為protected.否則應(yīng)該為private。
顯而易見,模板方法模式中的聲明為abstract的基本操作都是需要迫使子類去實現(xiàn)的,它們僅僅是為模板方法服務(wù)的。它們不應(yīng)該被抽象類(AbstractClass)所公開,所以它們應(yīng)該protected。
因此模板方法模式中,迫使子類實現(xiàn)的抽象方法應(yīng)該聲明為protected abstract。
4)模板方法與勾子方法(hookMethod)
模板方法模式的抽象類定義方法:
模板方法:一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總算法或一個總行為的方法。
基本方法:基本方法是實現(xiàn)算法各個步驟的方法,是模板方法的組成部分。基本方法如下:
?抽象方法(Abstract Method)
?具體方法(Concrete Method)
?鉤子方法(Hook Method):“掛鉤”方法和空方法,
hook方法在抽象類中的實現(xiàn)為空,是留給子類做一些可選的操作。如果某個子類需要一些特殊額外的操作,則可以實現(xiàn)hook方法,當然也可以完全不用理會,因為hook在抽象類中只是空方法而已。
1)鉤子方法的引入使得子類可以控制父類的行為。
2)最簡單的鉤子方法就是空方法,也可以在鉤子方法中定義一個默認的實現(xiàn),如果子類不覆蓋鉤子方法,則執(zhí)行父類的默認實現(xiàn)代碼。
3)比較復(fù)雜一點的鉤子方法可以對其他方法進行約束,這種鉤子方法通常返回一個boolean類型,即返回true或false,用來判斷是否執(zhí)行某一個基本方法。由子類來決定是否調(diào)用hook方法。
5) 小結(jié)
1)模板方法模式是一種類的行為型模式,在它的結(jié)構(gòu)圖中只有類之間的繼承關(guān)系,沒有對象關(guān)聯(lián)關(guān)系。
2)板方法模式是基于繼承的代碼復(fù)用基本技術(shù),模板方法模式的結(jié)構(gòu)和用法也是面向?qū)ο笤O(shè)計的核心之一。在模板方法模式中,可以將相同的代碼放在父類中,而將不同的方法實現(xiàn)放在不同的子類中。
3)在模板方法模式中,我們需要準備一個抽象類,將部分邏輯以具體方法以及具體構(gòu)造函數(shù)的形式實現(xiàn),然后聲明一些抽象方法來讓子類實現(xiàn)剩余的邏輯。不同的子類可以以不同的方式實現(xiàn)這些抽象方法,從而對剩余的邏輯有不同的實現(xiàn),這就是模板方法模式的用意。模板方法模式體現(xiàn)了面向?qū)ο蟮闹T多重要思想,是一種使用頻率較高的模式。
?
總結(jié)
以上是生活随笔為你收集整理的C++设计模式之模板模式(template)(行为型)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论文:图像分割的U-Net系列方法
- 下一篇: c++标准模板库:STL