作者 薛之謙chj 轉(zhuǎn)載請注明出處
我的知乎:https://zhuanlan.zhihu.com/c_1229107265379897344
內(nèi)容簡介:

定義:
Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.
將對象組合成樹形結(jié)構(gòu)以表示 “部分-整體” 的層次結(jié)構(gòu),使得用戶對單個對象和組合對象的使用具有一致性。
組合模式(Composite Pattern)?也稱為?整體-部分(Part-Whole)模式,它的宗旨是通過將單個對象(葉子節(jié)點)和組合對象(樹枝節(jié)點)用相同的接口進行表示,使得客戶對單個對象和組合對象的使用具有一致性。
組合模式?一般用來描述?整體?與?部分?的關(guān)系,它將對象組織到樹形結(jié)構(gòu)中,最頂層的節(jié)點稱為?根節(jié)點,根節(jié)點下面可以包含?樹枝節(jié)點?和?葉子節(jié)點,樹枝節(jié)點下面又可以包含?樹枝節(jié)點?和?葉子節(jié)點。如下圖所示:

由上圖可以看出,其實?根節(jié)點?和?樹枝節(jié)點?本質(zhì)上是同一種數(shù)據(jù)類型(藍色圓圈),可以作為容器使用;而?葉子節(jié)點?與?樹枝節(jié)點?在語義上不屬于同一種類型,但是在?組合模式?中,會把?樹枝節(jié)點?和?葉子節(jié)點?認為是同一種數(shù)據(jù)類型(用同一接口定義),讓它們具備一致行為。這樣,在?組合模式?中,整個樹形結(jié)構(gòu)中的對象都是同一種類型,帶來的一個好處就是客戶無需辨別?樹枝節(jié)點?還是?葉子節(jié)點,而是可以直接進行操作,給客戶使用帶來極大的便利。
組合模式?核心:借助同一接口,使葉子節(jié)點和樹枝節(jié)點的操作具備一致性。
主要解決
當子系統(tǒng)與其內(nèi)各個對象層次呈現(xiàn)樹形結(jié)構(gòu)時,可以使用?組合模式?讓該子系統(tǒng)內(nèi)各個對象層次的行為操作具備一致性??蛻舳耸褂迷撟酉到y(tǒng)內(nèi)任意一個層次對象時,無須進行區(qū)分,直接使用通用操作即可,為客戶端的使用帶來了便捷。
注:如果樹形結(jié)構(gòu)系統(tǒng)不使用?組合模式?進行架構(gòu),那么按照正常的思維邏輯,對該系統(tǒng)進行職責分析,按上文樹形結(jié)構(gòu)圖所示,該系統(tǒng)具備兩種對象層次類型:樹枝節(jié)點和葉子節(jié)點。那么我們就需要構(gòu)造兩種對應的類型,然后由于樹枝節(jié)點具備容器功能,因此樹枝節(jié)點類內(nèi)部需維護多個集合存儲其他對象層次(eg:List<Composite>,List<Leaf>),如果當前系統(tǒng)對象層次更復雜時,那么樹枝節(jié)點內(nèi)就又要增加對應的層次集合,這對樹枝節(jié)點的構(gòu)建帶來了巨大的復雜性,臃腫性以及不可擴展性。同時客戶端訪問該系統(tǒng)層次時,還需進行層次區(qū)分,這樣才能使用對應的行為,給客戶端的使用也帶來了巨大的復雜性。而如果使用?組合模式?構(gòu)建該系統(tǒng),由于?組合模式?抽取了系統(tǒng)各個層次的共性行為,具體層次只需按需實現(xiàn)所需行為即可,這樣子系統(tǒng)各個層次就都屬于同一種類型,所以樹枝節(jié)點只需維護一個集合(List<Component>)即可存儲系統(tǒng)所有層次內(nèi)容,并且客戶端也無需區(qū)分該系統(tǒng)各個層次對象,對內(nèi)系統(tǒng)架構(gòu)簡潔優(yōu)雅,對外接口精簡易用。
優(yōu)缺點
優(yōu)點
組合模式?屏蔽了對象系統(tǒng)的層次差異性(樹節(jié)點和葉子節(jié)點為不同類型),將客戶代碼與復雜的容器對象解耦,使得客戶端可以忽略層次間的差異,使用一致的行為控制不同層次。
在?組合模式?可以很方便地增加?樹枝節(jié)點?和?葉子節(jié)點?對象,并對現(xiàn)有類庫無侵入,符合?開閉原則;
缺點
如果類系統(tǒng)(樹形結(jié)構(gòu))過于龐大,雖然對不同層次都提供一致性操作,但客戶端仍需花費時間理清類之間的層次關(guān)系;
組合模式?在具體實現(xiàn)上違背了設(shè)計模式?接口隔離原則?或?依賴倒置原則;
使用場景
系統(tǒng)對象層次具備整體和部分,呈樹形結(jié)構(gòu),且要求具備統(tǒng)一行為(如樹形菜單,操作系統(tǒng)目錄結(jié)構(gòu),公司組織架構(gòu)等);
模式講解
組合模式?主要包含三種角色:
抽象根節(jié)點(Component):定義系統(tǒng)各層次對象的共有方法和屬性,可以預先定義一些默認行為和屬性;
樹枝節(jié)點(Composite):定義樹枝節(jié)點的行為,存儲子節(jié)點,組合樹枝節(jié)點和葉子節(jié)點形成一個樹形結(jié)構(gòu);
葉子節(jié)點(Leaf):葉子節(jié)點對象,其下再無分支,是系統(tǒng)層次遍歷的最小單位;
組合模式?在代碼具體實現(xiàn)上,有兩種不同的方式:
透明模式:把組合(樹節(jié)點)使用的方法放到統(tǒng)一行為(Component)中,讓不同層次(樹節(jié)點,葉子節(jié)點)的結(jié)構(gòu)都具備一致行為;其 UML 類圖如下所示:

安全模式:統(tǒng)一行為(Component)只規(guī)定系統(tǒng)各個層次的最基礎(chǔ)的一致行為,而把組合(樹節(jié)點)本身的方法(管理子類對象的添加,刪除等)放到自身當中;其 UML 類圖如下所示:

安全組合模式?把系統(tǒng)各層次公有的行為定義在?Component?中,把組合(樹節(jié)點)特有的行為(管理子類增加,刪除等)放到自身(Composite)中。這樣做的好處是接口定義職責清晰,符合設(shè)計模式?單一職責原則?和?接口隔離原則;缺點是客戶需要區(qū)分樹枝節(jié)點(Composite)和葉子節(jié)點(Leaf),這樣才能正確處理各個層次的操作,客戶端無法依賴抽象(Component),違背了設(shè)計模式?依賴倒置原則。
問:透明組合模式?和?安全組合模式?都有各自的優(yōu)點和缺點,那么我們應該優(yōu)先選擇哪一種呢?
答:既然?組合模式?會被分為兩種實現(xiàn),那么肯定是不同的場合某一種會更加適合,也即具體情況具體分析。透明組合模式?將公共接口封裝到抽象根節(jié)點(Component)中,那么系統(tǒng)所有節(jié)點就具備一致行為,所以如果當系統(tǒng)絕大多數(shù)層次具備相同的公共行為時,采用?透明組合模式?也許會更好(代價:為剩下少數(shù)層次節(jié)點引入不需要的方法);而如果當系統(tǒng)各個層次差異性行為較多或者樹節(jié)點層次相對穩(wěn)定(健壯)時,采用?安全組合模式
注:設(shè)計模式的出現(xiàn)并不是說我們要寫的代碼一定要遵循設(shè)計模式所要求的方方面面,這是不現(xiàn)實同時也是不可能的。設(shè)計模式的出現(xiàn),其實只是強調(diào)好的代碼所具備的一些特征(六大設(shè)計原則),這些特征對于項目開發(fā)是具備積極效應的,但不是說我們每實現(xiàn)一個類就一定要全部滿足設(shè)計模式的要求,如果真的存在完全滿足設(shè)計模式的要求,反而可能存在過度設(shè)計的嫌疑。同時,23種設(shè)計模式,其實都是嚴格依循設(shè)計模式六大原則進行設(shè)計,只是不同的模式在不同的場景中會更加適用。設(shè)計模式的理解應該重于意而不是形,真正編碼時,經(jīng)常使用的是某種設(shè)計模式的變形體,真正切合項目的模式才是正確的模式。
具體實現(xiàn):
組合模式有時又叫部分-整體模式在處理類似樹形結(jié)構(gòu)的問題時比較方便,看看關(guān)系圖:

直接來看代碼:


組合模式的應用
1. 何時使用
想表達“部分-整體”層次結(jié)構(gòu)(樹形結(jié)構(gòu))時
希望用戶忽略組合對象與單個對象的不同,用戶將統(tǒng)一的使用組合結(jié)構(gòu)中的所有對象
2. 方法
樹枝和葉子實現(xiàn)統(tǒng)一接口,樹枝內(nèi)部組合該接口
3. 優(yōu)點
高層模塊調(diào)用簡單。一棵樹形機構(gòu)中的所有節(jié)點都是Component,局部和整體對調(diào)用者來說沒有任何區(qū)別,高層模塊不必關(guān)心自己處理的是單個對象還是整個組合結(jié)構(gòu)。
節(jié)點自由增加
4. 缺點
使用組合模式時,其葉子和樹枝的聲明都是實現(xiàn)類,而不是接口,違反了依賴倒轉(zhuǎn)原則
5. 使用場景
維護和展示部分-整體關(guān)系的場景(如樹形菜單、文件和文件夾管理)
從一個整體中能夠獨立出部分模塊或功能的場景
將多個對象組合在一起進行操作,常用于表示樹形結(jié)構(gòu)中,例如二叉樹,數(shù)等。
6. 應用實例
Swing中,Button、Checkbox等組件都是樹葉,而Container容器是樹枝
文本編輯時,可以單個字編輯,也可以整段編輯,還可以全文編輯
文件復制時,可以一個一個文件復制,也可以整個文件夾復制