大家好久不見喲!忙了一個月終于可以回歸到設計模式了。前幾天肥羊蜀黍在玩室友鵬鵬的電腦,感嘆現在的年輕人真是精力旺盛,代碼寫得牛P就算了,還如此勤奮的學習各國文化和語言,他的DEF盤放滿各種學習資料,什么”日語學習“、”歐美風情“、“日韓文化”,我對日韓文化也頗感興趣,于是就點開了看看,怎么包了n層文件夾,點到最后....畫面女人看了臉紅,男人看了血脈噴張。好了,警察叔叔來了,要趕緊溜了,畢竟還是無證駕駛!!!
一、組合模式的概念
先來一段官方概念:組合多個對象形成樹形結構以表示“整體-部分”的結構層次。組合模式對單個對象(即葉子對象)和組合對象(即容器對象)的使用具有一致性。
二、案例實現
這句官方解釋也是有點抽象,為了能讓大家更好地理解組合模式,今天就以我們非常熟悉的電腦文件作為故事背景!!!管理文件的時候,我們不難發現,文件夾和文件的關系就像一棵“樹”,如下圖
1、非組合模式演示
看完這個圖,我們再構思一下架構,此時,鵬鵬一拍桌子說是這樣的,如下圖:
鵬鵬還順帶用5分鐘把代碼寫出來了,然而我陷入了沉思...通常來說,業務邏輯關系相對簡單、類之間的繼承關系少的時候可以用上圖的架構,但如果繼承關系有n層呢?如果項目有多個層次節點呢?如果像鵬鵬那么調皮喜歡把文件用n個文件夾包著呢?那咋辦?!此時的我開始抓狂,進而不知所措,想想好像也只能殺個室友祭天了!!!(此時鵬鵬在墻角瑟瑟發抖)
2、組合模式演示
還好組合模式的出現拯救了鵬鵬,組合模式分為安全組合模式和透明組合模式,首先介紹比較常用而且比較推崇的安全組合模式,UML如下圖:
2.1 安全組合模式
看了這個UML鵬鵬松了口氣,小命得保。工作量看著減少很多,接口沒有了,改為了抽象類,方法直接放在實現類中,好精簡有木有!用Component基類封裝一些通用的方法和屬性,Composite(整體)和Leaf(部分)繼承基類,分別實現各自的方法,使有復雜層次關系或樹狀結構的架構簡化為只有整體和部分兩大塊。接下來看看代碼演示:
Component.h
//通用屬性
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) float size;
//通用方法
- (instancetype)initWithName:(NSString *)name andSize:(float)size;
- (NSString *) getInfo;
Component.m
//方法實現
- (void)addFile:(Component *)file {
[self.sububdirectory addObject:file];
}
- (NSArray *)getSubdirectory {
return self.sububdirectory;
}
- (NSMutableArray *)sububdirectory {
if (!_sububdirectory) {
_sububdirectory = [NSMutableArray array];
}
return _sububdirectory;
}
//使用場景
- (void) compositePattern {
//d_disk 作為root
//鵬鵬的D盤學習資料最多,我們就來剖析D盤吧...嘻嘻嘻~~wow!!!1T學習資料哦
Composite *d_disk = [[Composite alloc] initWithName:@"鵬鵬的D盤" andSize:1024];
//鵬鵬真勤奮,這么多類型的學習資料
//這里暫且忽略中間包的n層文件夾吧
Composite *language_Jan = [[Composite alloc] initWithName:@"日語學習" andSize:0.5];
Composite *culture_Korea = [[Composite alloc] initWithName:@"韓國風俗文化" andSize:0.25];
//這部小電影...知道文件名叫啥不?so easy...耳熟能詳!跟著蜀黍大聲讀“雅蠛蝶!!!”
Leaf *video_Jan = [[Leaf alloc] initWithName:@"やめて" andSize:0.5];
//其他小電影名字太辣眼睛,這里就不做詳細介紹(車牌還是找吾力鵬鵬要)
Leaf *video_Jan1 = [[Leaf alloc] initWithName:@"小電影1" andSize:0.5];
Leaf *video_Jan2 = [[Leaf alloc] initWithName:@"小電影2" andSize:0.2];
Leaf *pic_Korea1 = [[Leaf alloc] initWithName:@"小圖片1" andSize:0.02];
Leaf *pic_Korea2 = [[Leaf alloc] initWithName:@"小圖片2" andSize:0.01];
//開始組裝樹
[d_disk addFile:language_Jan];
[d_disk addFile:culture_Korea];
[language_Jan addFile:video_Jan];
[language_Jan addFile:video_Jan1];
[language_Jan addFile:video_Jan2];
[culture_Korea addFile:pic_Korea1];
[culture_Korea addFile:pic_Korea2];
//遍歷硬盤里面的各個文件(小電影)
NSLog(@"%@",[d_disk getInfo]);
[self cp_traverseSubdiretory:[d_disk getSubdirectory]];
}
- (void) cp_traverseSubdiretory:(NSArray *)directory {
for (id obj in directory) {
if ([obj isKindOfClass:[Composite class]]) {
Composite *folder = obj;
NSLog(@"%@",[folder getInfo]);
//遞歸遍歷
[self cp_traverseSubdiretory:[folder getSubdirectory]];
}else{
Leaf *file = obj;
NSLog(@"%@",[file getInfo]);
}
}
}
運行結果如下:
組合模式demo[1295:17972] name:鵬鵬的D盤 | size:1024.0
組合模式demo[1295:17972] name:日語學習 | size:0.5
組合模式demo[1295:17972] name:やめて | size:0.5
組合模式demo[1295:17972] name:小電影1 | size:0.5
組合模式demo[1295:17972] name:小電影2 | size:0.200000
組合模式demo[1295:17972] name:韓國風俗文化 | size:0.25
組合模式demo[1295:17972] name:小圖片1 | size:0.02
組合模式demo[1295:17972] name:小圖片2 | size:0.01
上述就是安全組合模式的簡單演示。接下來就是透明組合模式的介紹。
2.2透明組合模式
透明組合模式和安全組合模式的區別不大,只有一點,Leaf和Composite的方法全部在基類Component實現,UML如下:
由于篇幅問題,透明組合模式的代碼演示就不放啦!
要詳細了解請到詳細代碼演示查看
三、總結分析
1、安全組合模式
- 優點:
安全組合模式是由基類管理通用方法,Leaf和Composite各自管理擁有的方法,能確保各個類都只有職責范圍內的方法,比如文件夾只會有添加文件的方法,不可能有打開文件的功能,視頻文件也只有播放視頻的功能,不可能有添加文件的功能。 - 缺點:
在代碼演示部分可以看到,在安全組合模式下需要對類進行判斷,如果繼承基類的子類數目多,就會造成過多if-else的判斷,影響代碼質量。如果子類數量較多的時候應慎重考慮。
2、透明組合模式
- 優點:
在透明組合模式下,由基類統一管理所有方法,因此不需要進行類型判斷轉換。該模式針對抽象類編程,上層(抽象基類)的調用不依賴下層(子類),基本遵循依賴倒置原則,方便擴展。 - 缺點:
透明模式的缺點也十分明顯,由于方法由基類統一管理,可能會出現類持有了職責范圍外的方法,例如一個文件類具有添加文件夾的方法,這顯然是不合理的,因此在子類中我們會重寫子類不應該擁有的方法,通常會重寫為空方法或拋出異常處理,這一定程度上違反了里氏替換原則,還一定程度加大了工作量。 - 總體來說,組合模式的優缺點都十分突出且極端,在使用前要結合需求進行分析,靈活運用,盲目套用設計模式反而會給自己挖坑哦!