iOS 設計模式之九(組合模式)

一、概念

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末同眯,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子唯鸭,更是在濱河造成了極大的恐慌须蜗,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件目溉,死亡現場離奇詭異明肮,居然都是意外死亡,警方通過查閱死者的電腦和手機缭付,發(fā)現死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門柿估,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陷猫,你說我怎么就攤上這事秫舌。” “怎么了烙丛?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵舅巷,是天一觀的道長。 經常有香客問我河咽,道長钠右,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任忘蟹,我火速辦了婚禮飒房,結果婚禮上,老公的妹妹穿的比我還像新娘媚值。我一直安慰自己狠毯,他們只是感情好,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布褥芒。 她就那樣靜靜地躺著嚼松,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锰扶。 梳的紋絲不亂的頭發(fā)上献酗,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音坷牛,去河邊找鬼罕偎。 笑死,一個胖子當著我的面吹牛京闰,可吹牛的內容都是我干的颜及。 我是一名探鬼主播甩苛,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼俏站!你這毒婦竟也來了讯蒲?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤乾翔,失蹤者是張志新(化名)和其女友劉穎爱葵,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體反浓,經...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡萌丈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了雷则。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辆雾。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖月劈,靈堂內的尸體忽然破棺而出度迂,到底是詐尸還是另有隱情,我是刑警寧澤猜揪,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布惭墓,位于F島的核電站,受9級特大地震影響而姐,放射性物質發(fā)生泄漏腊凶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一拴念、第九天 我趴在偏房一處隱蔽的房頂上張望钧萍。 院中可真熱鬧,春花似錦政鼠、人聲如沸风瘦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽万搔。三九已至,卻和暖如春官帘,著一層夾襖步出監(jiān)牢的瞬間瞬雹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工遏佣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人揽浙。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓状婶,卻偏偏與公主長得像意敛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子膛虫,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內容

  • 【學習難度:★★★☆☆草姻,使用頻率:★★★★☆】直接出處:組合模式梳理和學習:https://github.com/...
    BruceOuyang閱讀 1,000評論 0 1
  • 目錄 本文的結構如下: 引言 什么是組合模式 模式的結構 典型代碼 代碼示例 優(yōu)點和缺點 適用環(huán)境 模式應用 一、...
    w1992wishes閱讀 890評論 0 2
  • 本文的主要內容: 介紹組合模式 示例 組合模式總結 源碼分析組合模式的典型應用java.awt中的組合模式Java...
    小旋鋒的簡書閱讀 1,037評論 0 4
  • 組合模式(結構型) 一稍刀、概述 對于樹形結構撩独,當容器對象(如文件夾)的某一個方法被調用時,將遍歷整個樹形結構账月,尋找也...
    哈哈大圣閱讀 474評論 0 1
  • 剛沖完澡综膀,汗又冒了出來,臉上濕濕的局齿,剛涂的爽膚水估計已順著汗水流了出來剧劝。 這段時間,我晚上只是拍點爽膚水而已抓歼。暑假...
    涓子520閱讀 214評論 0 0