一、概念
1膝昆、模板方法模式的動機(jī)
? 在現(xiàn)實(shí)生活中丸边,很多事情都包含幾個相對固定的步驟,比如去公司工作荚孵,你需要先打開電腦妹窖,然后用電腦工作,最后關(guān)閉電腦回家收叶。在軟件開發(fā)中骄呼,也有類似的情況,某個方法的實(shí)現(xiàn)需要多個步驟(類似“工作”),其中有些步驟是固定的(類似“電腦開機(jī)”)蜓萄,而有些步驟并不固定(類似“具體工作”)隅茎。
? 為了提高代碼的復(fù)用性和系統(tǒng)的靈活性,可以使用一種稱之為模板方法模式的設(shè)計(jì)模式來對這類情況進(jìn)行設(shè)計(jì)嫉沽,在模板方法模式中辟犀,將實(shí)現(xiàn)功能的每一個步驟所對應(yīng)的方法稱為基本方法(比如“開機(jī)”、“具體工作”耻蛇、“關(guān)機(jī)”等)踪蹬,而調(diào)用這些基本方法同時定義基本方法的執(zhí)行次序的方法稱為模板方法(比如“在公司上班”)。
2臣咖、模板方法模式的定義
? 模板方法模式(Template Method Pattern):定義一個操作中算法的框架跃捣,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟夺蛇。
? 模板方法模式是一種基于繼承的代碼復(fù)用技術(shù)疚漆,它是一種類行為型模式。
3刁赦、模板方法模式的2個角色
1)AbstractClass(抽象類):在抽象類中定義了一系列基本操作(PrimitiveOperations)娶聘,這些基本操作可以是具體的,也可以是抽象的甚脉,每一個基本操作對應(yīng)算法的一個步驟丸升,在其子類中可以重定義或?qū)崿F(xiàn)這些步驟。同時牺氨,在抽象類中實(shí)現(xiàn)了一個模板方法(Template Method)狡耻,用于定義一個算法的框架,模板方法不僅可以調(diào)用在抽象類中實(shí)現(xiàn)的基本方法猴凹,也可以調(diào)用在抽象類的子類中實(shí)現(xiàn)的基本方法夷狰,還可以調(diào)用其他對象中的方法。
2)ConcreteClass(具體子類):它是抽象類的子類郊霎,用于實(shí)現(xiàn)在父類中聲明的抽象基本操作以完成子類特定算法的步驟沼头,也可以覆蓋在父類中已經(jīng)實(shí)現(xiàn)的具體基本操作。
4书劝、模板方法與基本方法的概念
模板方法:
? 一個模板方法是定義在抽象類中的进倍、把基本操作方法組合在一起形成一個總算法或一個總行為的方法。這個模板方法定義在抽象類中庄撮,并由子類不加以修改地完全繼承下來背捌。模板方法是一個具體方法,它給出了一個頂層邏輯框架洞斯,而邏輯的組成步驟在抽象類中可以是具體方法毡庆,也可以是抽象方法坑赡。由于模板方法是具體方法,因此模板方法模式中的抽象層只能是抽象類么抗,而不是接口毅否。
基本方法:
基本方法是實(shí)現(xiàn)算法各個步驟的方法,是模板方法的組成部分蝇刀∶樱基本方法又可以分為三種:
1)抽象方法(Abstract Method):一個抽象方法由抽象類聲明、由其具體子類實(shí)現(xiàn)吞琐。在C#和Java語言里一個抽象方法以abstract關(guān)鍵字標(biāo)識捆探。
2)具體方法(Concrete Method):一個具體方法由一個抽象類或具體類聲明并實(shí)現(xiàn),其子類可以進(jìn)行覆蓋也可以直接繼承站粟。
3)鉤子方法(Hook Method):一個鉤子方法由一個抽象類或具體類聲明并實(shí)現(xiàn)黍图,而其子類可能會加以擴(kuò)展。鉤子方法分兩類:第一類鉤子方法可以與一些具體步驟“掛鉤”奴烙,以實(shí)現(xiàn)在不同條件下執(zhí)行模板方法中的不同步驟助被,這類鉤子方法的返回類型通常是bool類型的,這類方法名一般為IsXXX()切诀;第二類鉤子方法就是實(shí)現(xiàn)體為空的具體方法揩环,這類鉤子方法的好處在于子類如果沒有覆蓋父類中定義的鉤子方法,編譯可以正常通過幅虑。
5丰滑、結(jié)構(gòu)圖
二、示例
? 在模板方法模式中倒庵,由于面向?qū)ο蟮亩鄳B(tài)性吨枉,子類對象在運(yùn)行時將覆蓋父類對象,子類中定義的方法也將覆蓋父類中定義的方法哄芜。因此程序在運(yùn)行時,子類的鉤子方法也將覆蓋父類的鉤子方法柬唯,從而可以通過在子類中實(shí)現(xiàn)的鉤子方法對父類方法的執(zhí)行進(jìn)行約束认臊,實(shí)現(xiàn)子類對父類行為的反向控制。
? 本Demo以程序員工作為例:
1)先創(chuàng)建一個Coder類锄奢,類中有模板方法和一系列基本方法失晴,表示抽象類;
2)然后創(chuàng)建Employee類和Leader類拘央,都繼承自Coder類涂屁,表示具體子類。
具體代碼如下:
Coder類:
// 程序猿:抽象類
@interface Coder : NSObject
- (void)work; //模板方法
- (void)startComputer; //具體方法
- (void)coding; //抽象方法灰伟,OC沒有abstract這個關(guān)鍵字拆又,這里選擇不實(shí)現(xiàn)方法
- (void)closeComputer; //具體方法
- (BOOL)isNeedCloseComputer; //鉤子方法
@end
@implementation Coder
- (void)work {
[self startComputer];
[self coding];
if ([self isNeedCloseComputer]) {
[self closeComputer];
}
}
- (void)startComputer {
NSLog(@"電腦開機(jī)");
}
- (void)closeComputer {
NSLog(@"電腦關(guān)機(jī)");
}
- (BOOL)isNeedCloseComputer {
NSLog(@"Windows電腦需要關(guān)機(jī)");
return YES;
}
@end
Employee類和Leader類:
// Employee 普通員工類
@interface Employee : Coder
@end
@implementation Employee
- (void)coding { // 實(shí)現(xiàn)抽象方法
NSLog(@"程序猿努力敲代碼");
}
@end
// Leader 領(lǐng)導(dǎo)類
@interface Leader : Coder
@end
@implementation Leader
- (void)coding { // 實(shí)現(xiàn)抽象方法
NSLog(@"領(lǐng)導(dǎo)指揮大家敲代碼");
}
- (BOOL)isNeedCloseComputer { // 重寫鉤子方法
NSLog(@"MacBook Pro不需要關(guān)機(jī)");
return NO;
}
@end
運(yùn)行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
Employee *employee = [Employee new];
[employee work];
NSLog(@"---------------------");
Leader *leader = [Leader new];
[leader work];
}
打印結(jié)果:
電腦開機(jī)
程序猿努力敲代碼
Windows電腦需要關(guān)機(jī)
電腦關(guān)機(jī)
---------------------
電腦開機(jī)
領(lǐng)導(dǎo)指揮大家敲代碼
MacBook Pro不需要關(guān)機(jī)
三儒旬、總結(jié)
? 模板方法模式是基于繼承的代碼復(fù)用技術(shù),廣泛應(yīng)用于框架設(shè)計(jì)中帖族,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化栈源,測試流程的設(shè)置等)。
1竖般、優(yōu)點(diǎn)
1甚垦、在父類中形式化地定義一個算法,而由它的子類來實(shí)現(xiàn)細(xì)節(jié)的處理涣雕,在子類實(shí)現(xiàn)詳細(xì)的處理算法時并不會改變算法中步驟的執(zhí)行次序艰亮。
2、模板方法模式是一種代碼復(fù)用技術(shù)挣郭,它在類庫設(shè)計(jì)中尤為重要迄埃,它提取了類庫中的公共行為,將公共行為放在父類中丈屹,而通過其子類來實(shí)現(xiàn)不同的行為调俘,它鼓勵我們恰當(dāng)使用繼承來實(shí)現(xiàn)代碼復(fù)用。
3旺垒、可實(shí)現(xiàn)一種反向控制結(jié)構(gòu)彩库,通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執(zhí)行。
4先蒋、在模板方法模式中可以通過子類來覆蓋父類的基本方法骇钦,不同的子類可以提供基本方法的不同實(shí)現(xiàn),更換和增加新的子類很方便竞漾,符合單一職責(zé)原則和開閉原則眯搭。
2、缺點(diǎn)
? 需要為每一個基本方法的不同實(shí)現(xiàn)提供一個子類业岁,如果父類中可變的基本方法太多鳞仙,將會導(dǎo)致類的個數(shù)增加,系統(tǒng)更加龐大笔时,設(shè)計(jì)也更加抽象棍好,此時,可結(jié)合橋接模式來進(jìn)行設(shè)計(jì)。
3、適用場景
1拓巧、對一些復(fù)雜的算法進(jìn)行分割盗迟,將其算法中固定不變的部分設(shè)計(jì)為模板方法和父類具體方法,而一些可以改變的細(xì)節(jié)由其子類來實(shí)現(xiàn)。即:一次性實(shí)現(xiàn)一個算法的不變部分,并將可變的行為留給子類來實(shí)現(xiàn)。
2低散、各子類中公共的行為應(yīng)被提取出來并集中到一個公共父類中以避免代碼重復(fù)俯邓。
3、需要通過子類來決定父類算法中某個步驟是否執(zhí)行谦纱,實(shí)現(xiàn)子類對父類的反向控制看成。
4、iOS應(yīng)用舉例
? 在Cocoa Touch框架中跨嘉,最常見的UIViewController川慌,如果需要適配屏幕旋轉(zhuǎn),那么必須重寫各種旋轉(zhuǎn)的方法祠乃,比如shouldAutorotate就是典型的鉤子方法梦重。還有UIView有個drawRect方法,如果需要執(zhí)行定制繪圖亮瓷,那么子類可以重寫這個方法來達(dá)到效果琴拧,drawRect也是鉤子方法。
Demo地址:iOS-Design-Patterns