一、概念
1酿炸、組合模式的動機
? 樹形結構在軟件中隨處可見,例如操作系統(tǒng)中的目錄結構涨冀、應用軟件中的菜單填硕、辦公系統(tǒng)中的公司組織結構等等,如何運用面向對象的方式來處理這種樹形結構是組合模式需要解決的問題鹿鳖。
? 組合模式通過一種巧妙的設計方案使得用戶可以一致性地處理整個樹形結構或者樹形結構的一部分扁眯,也可以一致性地處理樹形結構中的葉子節(jié)點(不包含子節(jié)點的節(jié)點)和容器節(jié)點(包含子節(jié)點的節(jié)點)。
2翅帜、組合模式的定義
? 組合模式(Composite Pattern):組合多個對象形成樹形結構以表示具有“整體—部分”關系的層次結構姻檀。組合模式對單個對象(即葉子對象)和組合對象(即容器對象)的使用具有一致性,組合模式又可以稱為“整體—部分”(Part-Whole)模式涝滴,它是一種對象結構型模式绣版。
3、組合模式的3個角色
1)Component(抽象構件):它可以是接口或抽象類歼疮,為葉子構件和容器構件對象聲明接口杂抽,在該角色中可以包含所有子類共有行為的聲明和實現。在抽象構件中定義了訪問及管理它的子構件的方法韩脏,如增加子構件默怨、刪除子構件、獲取子構件等骤素。
2)Leaf(葉子構件):它在組合結構中表示葉子節(jié)點對象,葉子節(jié)點沒有子節(jié)點愚屁,它實現了在抽象構件中定義的行為济竹。對于那些訪問及管理子構件的方法,可以通過異常等方式進行處理霎槐。
3)Composite(容器構件):它在組合結構中表示容器節(jié)點對象送浊,容器節(jié)點包含子節(jié)點,其子節(jié)點可以是葉子節(jié)點丘跌,也可以是容器節(jié)點袭景,它提供一個集合用于存儲子節(jié)點,實現了在抽象構件中定義的行為闭树,包括那些訪問及管理子構件的方法耸棒,在其業(yè)務方法中可以遞歸調用其子節(jié)點的業(yè)務方法。
? 組合模式的關鍵是定義了一個抽象構件類报辱,它既可以代表葉子与殃,又可以代表容器,而客戶端針對該抽象構件類進行編程,無須知道它到底表示的是葉子還是容器幅疼,可以對其進行統(tǒng)一處理米奸。同時容器對象與抽象構件類之間還建立一個聚合關聯(lián)關系,在容器對象中既可以包含葉子爽篷,也可以包含容器悴晰,以此實現遞歸組合,形成一個樹形結構逐工。
4铡溪、結構圖
二、示例
1)創(chuàng)建一個Company協(xié)議钻弄,里面有一些公司的添加和刪除方法佃却,表示Component抽象構件;
2)創(chuàng)建ConcreteCompany類窘俺,遵循Company協(xié)議饲帅,并實現協(xié)議的方法,表示Composite容器構件瘤泪;
3)創(chuàng)建三個類HRDepartment灶泵、ITDepartment和ServiceDepartment,都遵循Company協(xié)議对途,并實現協(xié)議的方法赦邻,表示Leaf葉子構件;
Company協(xié)議:
// 抽象構件Component
@protocol Company <NSObject>
@property(nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
- (void)add:(id<Company>)company;
- (void)remove:(id<Company>)company;
- (void)work;
@end
typedef id<Company> Company;
ConcreteCompany類:
// 容器構件Composite
// .h文件
@interface ConcreteCompany : NSObject<Company>
@end
// .m文件
@interface ConcreteCompany ()
@property(nonatomic, strong) NSMutableArray *childList;
@end
@implementation ConcreteCompany
@synthesize name = _name;
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_childList = [NSMutableArray array];
_name = name;
}
return self;
}
- (void)add:(id<Company>)company {
[self.childList addObject:company];
}
- (void)remove:(id<Company>)company {
if ([self.childList containsObject:company]) {
[self.childList removeObject:company];
}
}
- (void)work {
NSLog(@"%@-----------", self.name);
for (Company company in self.childList) { //遞歸遍歷
[company work];
}
}
@end
HRDepartment实檀、ITDepartment和ServiceDepartment類:
// 葉子構件Leaf
// HRDepartment
@interface HRDepartment : NSObject<Company>
@end
@implementation HRDepartment
@synthesize name = _name;
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)add:(id<Company>)company {}
- (void)remove:(id<Company>)company {}
- (void)work {
NSLog(@"%@:give an offer", self.name);
}
@end
// ITDepartment
@interface ITDepartment : NSObject<Company>
@end
@implementation ITDepartment
@synthesize name = _name;
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)add:(id<Company>)company {}
- (void)remove:(id<Company>)company {}
- (void)work {
NSLog(@"%@:coding", self.name);
}
@end
// ServiceDepartment
@interface ServiceDepartment : NSObject<Company>
@end
@implementation ServiceDepartment
@synthesize name = _name;
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = name;
}
return self;
}
- (void)add:(id<Company>)company {}
- (void)remove:(id<Company>)company {}
- (void)work {
NSLog(@"%@:call customers", self.name);
}
@end
運行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
ConcreteCompany *apple = [[ConcreteCompany alloc] initWithName:@"蘋果公司"];
ConcreteCompany *AmericanCompany = [[ConcreteCompany alloc] initWithName:@"美國分公司"];
ConcreteCompany *ChinaCompany = [[ConcreteCompany alloc] initWithName:@"中國分公司"];
[apple add:AmericanCompany];
[apple add:ChinaCompany];
[ChinaCompany add:[[ServiceDepartment alloc] initWithName:@"中國分部客服部門"]];
[AmericanCompany add:[[HRDepartment alloc] initWithName:@"美國分部HR部門"]];
[AmericanCompany add:[[ITDepartment alloc] initWithName:@"美國分部IT部門"]];
[AmericanCompany add:[[ServiceDepartment alloc] initWithName:@"美國分部客服部門"]];
[apple work];
// [AmericanCompany work];
// [ChinaCompany work];
}
打印結果:
蘋果公司-----------
美國分公司-----------
美國分部HR部門:give an offer
美國分部IT部門:coding
美國分部客服部門:call customers
中國分公司-----------
中國分部客服部門:call customers
三惶洲、總結
? 組合模式使用面向對象的思想來實現樹形結構的構建與處理,描述了如何將容器對象和葉子對象進行遞歸組合膳犹,實現簡單恬吕,靈活性好。由于在軟件開發(fā)中存在大量的樹形結構须床,因此組合模式是一種使用頻率較高的結構型設計模式铐料。
1、優(yōu)點
1豺旬、組合模式可以清楚地定義分層次的復雜對象钠惩,表示對象的全部或部分層次,它讓客戶端忽略了層次的差異族阅,方便對整個層次結構進行控制篓跛。
2、客戶端可以一致地使用一個組合結構或其中單個對象坦刀,不必關心處理的是單個對象還是整個組合結構举塔,簡化了客戶端代碼绑警。
3、在組合模式中增加新的容器構件和葉子構件都很方便央渣,無須對現有類庫進行任何修改计盒,符合“開閉原則”。
4芽丹、組合模式為樹形結構的面向對象實現提供了一種靈活的解決方案北启,通過葉子對象和容器對象的遞歸組合,可以形成復雜的樹形結構拔第,但對樹形結構的控制卻非常簡單咕村。
2、缺點
? 在增加新構件時很難對容器中的構件類型進行限制蚊俺。有時候我們希望一個容器中只能有某些特定類型的對象懈涛,例如在某個文件夾中只能包含文本文件,使用組合模式時泳猬,不能依賴類型系統(tǒng)來施加這些約束批钠,因為它們都來自于相同的抽象層,在這種情況下得封,必須通過在運行時進行類型檢查來實現埋心,這個實現過程較為復雜。
3忙上、適用場景
1拷呆、在具有整體和部分的層次結構中,希望通過一種方式忽略整體與部分的差異疫粥,客戶端可以一致地對待它們茬斧。
2、在一個使用面向對象語言開發(fā)的系統(tǒng)中需要處理一個樹形結構梗逮。
3啥供、在一個系統(tǒng)中能夠分離出葉子對象和容器對象,而且它們的類型不固定库糠,需要增加一些新的類型。
4涮毫、iOS應用舉例
? 在Cocoa Touch框架中瞬欧,UIView被組織成一個組合結構,每個UIView的實例可以包含UIView的其他實例罢防,形成統(tǒng)一的樹形結構艘虎,讓客戶端對單個UIView對象和UIView的組合統(tǒng)一對待。
? 視圖的樹形結構的根部是一個UIWindow對象和它的內容視圖咒吐,添加進來的其他UIView是它的子視圖野建,被添加的子視圖可以包含其他的視圖而變成自己的子視圖的父視圖属划。UIView對象只能有一個父視圖,可以有零到多個子視圖候生。
Demo地址:iOS-Design-Patterns