
大家好久不見喲!忙了一個(gè)月終于可以回歸到設(shè)計(jì)模式了。前幾天肥羊蜀黍在玩室友鵬鵬的電腦,感嘆現(xiàn)在的年輕人真是精力旺盛,代碼寫得牛P就算了,還如此勤奮的學(xué)習(xí)各國文化和語言,他的DEF盤放滿各種學(xué)習(xí)資料,什么”日語學(xué)習(xí)“、”歐美風(fēng)情“、“日韓文化”,我對日韓文化也頗感興趣,于是就點(diǎn)開了看看,怎么包了n層文件夾,點(diǎn)到最后....畫面女人看了臉紅,男人看了血脈噴張。好了,警察叔叔來了,要趕緊溜了,畢竟還是無證駕駛?。?!
一、組合模式的概念
先來一段官方概念:組合多個(gè)對象形成樹形結(jié)構(gòu)以表示“整體-部分”的結(jié)構(gòu)層次。組合模式對單個(gè)對象(即葉子對象)和組合對象(即容器對象)的使用具有一致性。
二、案例實(shí)現(xiàn)
這句官方解釋也是有點(diǎn)抽象,為了能讓大家更好地理解組合模式,今天就以我們非常熟悉的電腦文件作為故事背景?。。」芾砦募臅r(shí)候,我們不難發(fā)現(xiàn),文件夾和文件的關(guān)系就像一棵“樹”,如下圖

1、非組合模式演示
看完這個(gè)圖,我們再構(gòu)思一下架構(gòu),此時(shí),鵬鵬一拍桌子說是這樣的,如下圖:

鵬鵬還順帶用5分鐘把代碼寫出來了,然而我陷入了沉思...通常來說,業(yè)務(wù)邏輯關(guān)系相對簡單、類之間的繼承關(guān)系少的時(shí)候可以用上圖的架構(gòu),但如果繼承關(guān)系有n層呢?如果項(xiàng)目有多個(gè)層次節(jié)點(diǎn)呢?如果像鵬鵬那么調(diào)皮喜歡把文件用n個(gè)文件夾包著呢?那咋辦?!此時(shí)的我開始抓狂,進(jìn)而不知所措,想想好像也只能殺個(gè)室友祭天了?。。。ù藭r(shí)鵬鵬在墻角瑟瑟發(fā)抖)
2、組合模式演示
還好組合模式的出現(xiàn)拯救了鵬鵬,組合模式分為安全組合模式和透明組合模式,首先介紹比較常用而且比較推崇的安全組合模式,UML如下圖:
2.1 安全組合模式

看了這個(gè)UML鵬鵬松了口氣,小命得保。工作量看著減少很多,接口沒有了,改為了抽象類,方法直接放在實(shí)現(xiàn)類中,好精簡有木有!用Component基類封裝一些通用的方法和屬性,Composite(整體)和Leaf(部分)繼承基類,分別實(shí)現(xiàn)各自的方法,使有復(fù)雜層次關(guān)系或樹狀結(jié)構(gòu)的架構(gòu)簡化為只有整體和部分兩大塊。接下來看看代碼演示:
Component.h
//通用屬性
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) float size;
//通用方法
- (instancetype)initWithName:(NSString *)name andSize:(float)size;
- (NSString *) getInfo;
Component.m
//方法實(shí)現(xiàn)
- (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盤學(xué)習(xí)資料最多,我們就來剖析D盤吧...嘻嘻嘻~~wow?。?!1T學(xué)習(xí)資料哦
Composite *d_disk = [[Composite alloc] initWithName:@"鵬鵬的D盤" andSize:1024];
//鵬鵬真勤奮,這么多類型的學(xué)習(xí)資料
//這里暫且忽略中間包的n層文件夾吧
Composite *language_Jan = [[Composite alloc] initWithName:@"日語學(xué)習(xí)" andSize:0.5];
Composite *culture_Korea = [[Composite alloc] initWithName:@"韓國風(fēng)俗文化" andSize:0.25];
//這部小電影...知道文件名叫啥不?so easy...耳熟能詳!跟著蜀黍大聲讀“雅蠛蝶?。?!”
Leaf *video_Jan = [[Leaf alloc] initWithName:@"やめて" andSize:0.5];
//其他小電影名字太辣眼睛,這里就不做詳細(xì)介紹(車牌還是找吾力鵬鵬要)
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];
//遍歷硬盤里面的各個(gè)文件(小電影)
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]);
}
}
}
運(yùn)行結(jié)果如下:
組合模式demo[1295:17972] name:鵬鵬的D盤 | size:1024.0
組合模式demo[1295:17972] name:日語學(xué)習(xí) | 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:韓國風(fēng)俗文化 | size:0.25
組合模式demo[1295:17972] name:小圖片1 | size:0.02
組合模式demo[1295:17972] name:小圖片2 | size:0.01
上述就是安全組合模式的簡單演示。接下來就是透明組合模式的介紹。
2.2透明組合模式
透明組合模式和安全組合模式的區(qū)別不大,只有一點(diǎn),Leaf和Composite的方法全部在基類Component實(shí)現(xiàn),UML如下:

由于篇幅問題,透明組合模式的代碼演示就不放啦!
要詳細(xì)了解請到詳細(xì)代碼演示查看
三、總結(jié)分析
1、安全組合模式
- 優(yōu)點(diǎn):
安全組合模式是由基類管理通用方法,Leaf和Composite各自管理擁有的方法,能確保各個(gè)類都只有職責(zé)范圍內(nèi)的方法,比如文件夾只會有添加文件的方法,不可能有打開文件的功能,視頻文件也只有播放視頻的功能,不可能有添加文件的功能。 - 缺點(diǎn):
在代碼演示部分可以看到,在安全組合模式下需要對類進(jìn)行判斷,如果繼承基類的子類數(shù)目多,就會造成過多if-else的判斷,影響代碼質(zhì)量。如果子類數(shù)量較多的時(shí)候應(yīng)慎重考慮。
2、透明組合模式
- 優(yōu)點(diǎn):
在透明組合模式下,由基類統(tǒng)一管理所有方法,因此不需要進(jìn)行類型判斷轉(zhuǎn)換。該模式針對抽象類編程,上層(抽象基類)的調(diào)用不依賴下層(子類),基本遵循依賴倒置原則,方便擴(kuò)展。 - 缺點(diǎn):
透明模式的缺點(diǎn)也十分明顯,由于方法由基類統(tǒng)一管理,可能會出現(xiàn)類持有了職責(zé)范圍外的方法,例如一個(gè)文件類具有添加文件夾的方法,這顯然是不合理的,因此在子類中我們會重寫子類不應(yīng)該擁有的方法,通常會重寫為空方法或拋出異常處理,這一定程度上違反了里氏替換原則,還一定程度加大了工作量。 - 總體來說,組合模式的優(yōu)缺點(diǎn)都十分突出且極端,在使用前要結(jié)合需求進(jìn)行分析,靈活運(yùn)用,盲目套用設(shè)計(jì)模式反而會給自己挖坑哦!