一、概念
1、模板方法模式的動機
? 在現實生活中,很多事情都包含幾個相對固定的步驟,比如去公司工作,你需要先打開電腦,然后用電腦工作,最后關閉電腦回家。在軟件開發中,也有類似的情況,某個方法的實現需要多個步驟(類似“工作”),其中有些步驟是固定的(類似“電腦開機”),而有些步驟并不固定(類似“具體工作”)。
? 為了提高代碼的復用性和系統的靈活性,可以使用一種稱之為模板方法模式的設計模式來對這類情況進行設計,在模板方法模式中,將實現功能的每一個步驟所對應的方法稱為基本方法(比如“開機”、“具體工作”、“關機”等),而調用這些基本方法同時定義基本方法的執行次序的方法稱為模板方法(比如“在公司上班”)。
2、模板方法模式的定義
? 模板方法模式(Template Method Pattern):定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
? 模板方法模式是一種基于繼承的代碼復用技術,它是一種類行為型模式。
3、模板方法模式的2個角色
1)AbstractClass(抽象類):在抽象類中定義了一系列基本操作(PrimitiveOperations),這些基本操作可以是具體的,也可以是抽象的,每一個基本操作對應算法的一個步驟,在其子類中可以重定義或實現這些步驟。同時,在抽象類中實現了一個模板方法(Template Method),用于定義一個算法的框架,模板方法不僅可以調用在抽象類中實現的基本方法,也可以調用在抽象類的子類中實現的基本方法,還可以調用其他對象中的方法。
2)ConcreteClass(具體子類):它是抽象類的子類,用于實現在父類中聲明的抽象基本操作以完成子類特定算法的步驟,也可以覆蓋在父類中已經實現的具體基本操作。
4、模板方法與基本方法的概念
模板方法:
? 一個模板方法是定義在抽象類中的、把基本操作方法組合在一起形成一個總算法或一個總行為的方法。這個模板方法定義在抽象類中,并由子類不加以修改地完全繼承下來。模板方法是一個具體方法,它給出了一個頂層邏輯框架,而邏輯的組成步驟在抽象類中可以是具體方法,也可以是抽象方法。由于模板方法是具體方法,因此模板方法模式中的抽象層只能是抽象類,而不是接口。
基本方法:
基本方法是實現算法各個步驟的方法,是模板方法的組成部分。基本方法又可以分為三種:
1)抽象方法(Abstract Method):一個抽象方法由抽象類聲明、由其具體子類實現。在C#和Java語言里一個抽象方法以abstract關鍵字標識。
2)具體方法(Concrete Method):一個具體方法由一個抽象類或具體類聲明并實現,其子類可以進行覆蓋也可以直接繼承。
3)鉤子方法(Hook Method):一個鉤子方法由一個抽象類或具體類聲明并實現,而其子類可能會加以擴展。鉤子方法分兩類:第一類鉤子方法可以與一些具體步驟“掛鉤”,以實現在不同條件下執行模板方法中的不同步驟,這類鉤子方法的返回類型通常是bool類型的,這類方法名一般為IsXXX();第二類鉤子方法就是實現體為空的具體方法,這類鉤子方法的好處在于子類如果沒有覆蓋父類中定義的鉤子方法,編譯可以正常通過。
5、結構圖
二、示例
? 在模板方法模式中,由于面向對象的多態性,子類對象在運行時將覆蓋父類對象,子類中定義的方法也將覆蓋父類中定義的方法。因此程序在運行時,子類的鉤子方法也將覆蓋父類的鉤子方法,從而可以通過在子類中實現的鉤子方法對父類方法的執行進行約束,實現子類對父類行為的反向控制。
? 本Demo以程序員工作為例:
1)先創建一個Coder類,類中有模板方法和一系列基本方法,表示抽象類;
2)然后創建Employee類和Leader類,都繼承自Coder類,表示具體子類。
具體代碼如下:
Coder類:
// 程序猿:抽象類
@interface Coder : NSObject
- (void)work; //模板方法
- (void)startComputer; //具體方法
- (void)coding; //抽象方法,OC沒有abstract這個關鍵字,這里選擇不實現方法
- (void)closeComputer; //具體方法
- (BOOL)isNeedCloseComputer; //鉤子方法
@end
@implementation Coder
- (void)work {
[self startComputer];
[self coding];
if ([self isNeedCloseComputer]) {
[self closeComputer];
}
}
- (void)startComputer {
NSLog(@"電腦開機");
}
- (void)closeComputer {
NSLog(@"電腦關機");
}
- (BOOL)isNeedCloseComputer {
NSLog(@"Windows電腦需要關機");
return YES;
}
@end
Employee類和Leader類:
// Employee 普通員工類
@interface Employee : Coder
@end
@implementation Employee
- (void)coding { // 實現抽象方法
NSLog(@"程序猿努力敲代碼");
}
@end
// Leader 領導類
@interface Leader : Coder
@end
@implementation Leader
- (void)coding { // 實現抽象方法
NSLog(@"領導指揮大家敲代碼");
}
- (BOOL)isNeedCloseComputer { // 重寫鉤子方法
NSLog(@"MacBook Pro不需要關機");
return NO;
}
@end
運行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
Employee *employee = [Employee new];
[employee work];
NSLog(@"---------------------");
Leader *leader = [Leader new];
[leader work];
}
打印結果:
電腦開機
程序猿努力敲代碼
Windows電腦需要關機
電腦關機
---------------------
電腦開機
領導指揮大家敲代碼
MacBook Pro不需要關機
三、總結
? 模板方法模式是基于繼承的代碼復用技術,廣泛應用于框架設計中,以確保通過父類來控制處理流程的邏輯順序(如框架的初始化,測試流程的設置等)。
1、優點
1、在父類中形式化地定義一個算法,而由它的子類來實現細節的處理,在子類實現詳細的處理算法時并不會改變算法中步驟的執行次序。
2、模板方法模式是一種代碼復用技術,它在類庫設計中尤為重要,它提取了類庫中的公共行為,將公共行為放在父類中,而通過其子類來實現不同的行為,它鼓勵我們恰當使用繼承來實現代碼復用。
3、可實現一種反向控制結構,通過子類覆蓋父類的鉤子方法來決定某一特定步驟是否需要執行。
4、在模板方法模式中可以通過子類來覆蓋父類的基本方法,不同的子類可以提供基本方法的不同實現,更換和增加新的子類很方便,符合單一職責原則和開閉原則。
2、缺點
? 需要為每一個基本方法的不同實現提供一個子類,如果父類中可變的基本方法太多,將會導致類的個數增加,系統更加龐大,設計也更加抽象,此時,可結合橋接模式來進行設計。
3、適用場景
1、對一些復雜的算法進行分割,將其算法中固定不變的部分設計為模板方法和父類具體方法,而一些可以改變的細節由其子類來實現。即:一次性實現一個算法的不變部分,并將可變的行為留給子類來實現。
2、各子類中公共的行為應被提取出來并集中到一個公共父類中以避免代碼重復。
3、需要通過子類來決定父類算法中某個步驟是否執行,實現子類對父類的反向控制。
4、iOS應用舉例
? 在Cocoa Touch框架中,最常見的UIViewController,如果需要適配屏幕旋轉,那么必須重寫各種旋轉的方法,比如shouldAutorotate就是典型的鉤子方法。還有UIView有個drawRect方法,如果需要執行定制繪圖,那么子類可以重寫這個方法來達到效果,drawRect也是鉤子方法。
Demo地址:iOS-Design-Patterns