組合模式

簡介

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é)點)用相同的接口進(jìn)行表示,使得客戶對單個對象和組合對象的使用具有一致性铸屉。

組合模式 一般用來描述 整體部分 的關(guān)系,它將對象組織到樹形結(jié)構(gòu)中钳枕,最頂層的節(jié)點稱為 根節(jié)點横朋,根節(jié)點下面可以包含 樹枝節(jié)點葉子節(jié)點,樹枝節(jié)點下面又可以包含 樹枝節(jié)點葉子節(jié)點兑燥。如下圖所示:

樹形結(jié)構(gòu)

由上圖可以看出亮瓷,其實 根節(jié)點樹枝節(jié)點 本質(zhì)上是同一種數(shù)據(jù)類型(藍(lán)色圓圈),可以作為容器使用降瞳;而 葉子節(jié)點樹枝節(jié)點 在語義上不屬于同一種類型嘱支,但是在 組合模式 中,會把 樹枝節(jié)點葉子節(jié)點 認(rèn)為是同一種數(shù)據(jù)類型(用同一接口定義)挣饥,讓它們具備一致行為除师。這樣,在 組合模式 中扔枫,整個樹形結(jié)構(gòu)中的對象都是同一種類型汛聚,帶來的一個好處就是客戶無需辨別 樹枝節(jié)點 還是 葉子節(jié)點,而是可以直接進(jìn)行操作短荐,給客戶使用帶來極大的便利倚舀。

組合模式 核心:借助同一接口,使葉子節(jié)點和樹枝節(jié)點的操作具備一致性忍宋。

主要解決

當(dāng)子系統(tǒng)與其內(nèi)各個對象層次呈現(xiàn)樹形結(jié)構(gòu)時瞄桨,可以使用 組合模式 讓該子系統(tǒng)內(nèi)各個對象層次的行為操作具備一致性⊙茸伲客戶端使用該子系統(tǒng)內(nèi)任意一個層次對象時芯侥,無須進(jìn)行區(qū)分,直接使用通用操作即可,為客戶端的使用帶來了便捷柱查。

:如果樹形結(jié)構(gòu)系統(tǒng)不使用 組合模式 進(jìn)行架構(gòu)廓俭,那么按照正常的思維邏輯,對該系統(tǒng)進(jìn)行職責(zé)分析唉工,按上文樹形結(jié)構(gòu)圖所示研乒,該系統(tǒng)具備兩種對象層次類型:樹枝節(jié)點和葉子節(jié)點。那么我們就需要構(gòu)造兩種對應(yīng)的類型淋硝,然后由于樹枝節(jié)點具備容器功能雹熬,因此樹枝節(jié)點類內(nèi)部需維護(hù)多個集合存儲其他對象層次(eg:List<Composite>,List<Leaf>),如果當(dāng)前系統(tǒng)對象層次更復(fù)雜時谣膳,那么樹枝節(jié)點內(nèi)就又要增加對應(yīng)的層次集合竿报,這對樹枝節(jié)點的構(gòu)建帶來了巨大的復(fù)雜性,臃腫性以及不可擴(kuò)展性继谚。同時客戶端訪問該系統(tǒng)層次時烈菌,還需進(jìn)行層次區(qū)分,這樣才能使用對應(yīng)的行為花履,給客戶端的使用也帶來了巨大的復(fù)雜性芽世。而如果使用 組合模式 構(gòu)建該系統(tǒng),由于 組合模式 抽取了系統(tǒng)各個層次的共性行為诡壁,具體層次只需按需實現(xiàn)所需行為即可济瓢,這樣子系統(tǒng)各個層次就都屬于同一種類型,所以樹枝節(jié)點只需維護(hù)一個集合(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é)點為不同類型)宠漩,將客戶代碼與復(fù)雜的容器對象解耦,使得客戶端可以忽略層次間的差異懊直,使用一致的行為控制不同層次扒吁。
  • 組合模式 可以很方便地增加 樹枝節(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)各層次對象的共有方法和屬性当辐,可以預(yù)先定義一些默認(rèn)行為和屬性;
  • 樹枝節(jié)點(Composite):定義樹枝節(jié)點的行為鲤看,存儲子節(jié)點缘揪,組合樹枝節(jié)點和葉子節(jié)點形成一個樹形結(jié)構(gòu);
  • 葉子節(jié)點(Leaf):葉子節(jié)點對象义桂,其下再無分支找筝,是系統(tǒng)層次遍歷的最小單位;

組合模式 在代碼具體實現(xiàn)上慷吊,有兩種不同的方式:

  1. 透明模式:把組合(樹節(jié)點)使用的方法放到統(tǒng)一行為(Component)中袖裕,讓不同層次(樹節(jié)點,葉子節(jié)點)的結(jié)構(gòu)都具備一致行為罢浇;其 UML 類圖如下所示:
透明組合模式

透明組合模式 把所有公共方法都定義在 Component 中陆赋,這樣做的好處是客戶端無需分辨是葉子節(jié)點(Leaf)和樹枝節(jié)點(Composite)沐祷,它們具備完全一致的接口嚷闭;缺點是葉子節(jié)點(Leaf)會繼承得到一些它所不需要(管理子類操作的方法)的方法,這與設(shè)計模式 接口隔離原則 相違背赖临。

透明組合模式 通用代碼如下所示:

package com.yn.design_pattern.composite.universal.transparent;

import java.util.ArrayList;
import java.util.List;

class Client {
    public static void main(String[] args) {
        // 來一個根節(jié)點
        Component root = new Composite("root");
        // 來一個樹枝節(jié)點
        Component branchA = new Composite("---branchA");
        Component branchB = new Composite("------branchB");
        // 來一個葉子節(jié)點
        Component leafA = new Leaf("------leafA");
        Component leafB = new Leaf("---------leafB");
        Component leafC = new Leaf("---leafC");

        root.addChild(branchA);
        root.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);

        String result = root.operation();
        System.out.println(result);

    }

    // 抽象根節(jié)點
    static abstract class Component {
        protected String name;

        public Component(String name) {
            this.name = name;
        }

        public abstract String operation();

        public boolean addChild(Component component) {
            throw new UnsupportedOperationException("addChild not supported!");
        }

        public boolean removeChild(Component component) {
            throw new UnsupportedOperationException("removeChild not supported!");
        }

        public Component getChild(int index) {
            throw new UnsupportedOperationException("getChild not supported!");
        }
    }

    // 樹節(jié)點
    static class Composite extends Component {
        private List<Component> mComponents;

        public Composite(String name) {
            super(name);
            this.mComponents = new ArrayList<>();
        }

        @Override
        public String operation() {
            StringBuilder builder = new StringBuilder(this.name);
            for (Component component : this.mComponents) {
                builder.append("\n");
                builder.append(component.operation());
            }
            return builder.toString();
        }

        @Override
        public boolean addChild(Component component) {
            return this.mComponents.add(component);
        }

        @Override
        public boolean removeChild(Component component) {
            return this.mComponents.remove(component);
        }

        @Override
        public Component getChild(int index) {
            return this.mComponents.get(index);
        }
    }

    //葉子節(jié)點
    static class Leaf extends Component {

        public Leaf(String name) {
            super(name);
        }

        @Override
        public String operation() {
            return this.name;
        }
    }
}

透明組合模式 中胞锰,由于 Component 包含葉子節(jié)點所不需要的方法,因此兢榨,我們直接將這些方法默認(rèn)拋出UnsupportedOperationException異常嗅榕。

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

安全組合模式 把系統(tǒng)各層次公有的行為定義在 Component 中,把組合(樹節(jié)點)特有的行為(管理子類增加吟逝,刪除等)放到自身(Composite)中帽蝶。這樣做的好處是接口定義職責(zé)清晰,符合設(shè)計模式 單一職責(zé)原則接口隔離原則块攒;缺點是客戶需要區(qū)分樹枝節(jié)點(Composite)和葉子節(jié)點(Leaf)励稳,這樣才能正確處理各個層次的操作,客戶端無法依賴抽象(Component)囱井,違背了設(shè)計模式 依賴倒置原則驹尼。

安全組合模式 的通用代碼相對 透明組合模式 而言,需要進(jìn)行如下修改:

  • 修改 Component 代碼:只保留各層次公有行為:
    // 抽象根節(jié)點
    static abstract class Component {
        protected String name;

        public Component(String name) {
            this.name = name;
        }

        public abstract String operation();
    }
  • 修改客戶端代碼:將樹枝節(jié)點類型更改為 Composite 類型庞呕,以便獲取管理子類操作的方法:
class Client {
    public static void main(String[] args) {
        // 來一個根節(jié)點
        Composite root = new Composite("root");
        // 來一個樹枝節(jié)點
        Composite branchA = new Composite("---branchA");
        Composite branchB = new Composite("------branchB");
        // 來一個葉子節(jié)點
        Component leafA = new Leaf("------leafA");
        Component leafB = new Leaf("---------leafB");
        Component leafC = new Leaf("---leafC");

        root.addChild(branchA);
        root.addChild(leafC);
        branchA.addChild(leafA);
        branchA.addChild(branchB);
        branchB.addChild(leafB);

        String result = root.operation();
        System.out.println(result);
    }
}

上述例子的運行結(jié)果如下:

root
---branchA
------leafA
------branchB
---------leafB
---leafC

運行結(jié)果顯示:root 包含 branchA 和 leafC新翎;branchA 包含 leafA 和 branchB;branchB 包含 leafB。

這樣我們就使用 組合模式 模擬了一個樹形結(jié)構(gòu)系統(tǒng)料祠。

透明組合模式安全組合模式 都有各自的優(yōu)點和缺點骆捧,那么我們應(yīng)該優(yōu)先選擇哪一種呢?

:既然 組合模式 會被分為兩種實現(xiàn)髓绽,那么肯定是不同的場合某一種會更加適合敛苇,也即具體情況具體分析。透明組合模式 將公共接口封裝到抽象根節(jié)點(Component)中顺呕,那么系統(tǒng)所有節(jié)點就具備一致行為枫攀,所以如果當(dāng)系統(tǒng)絕大多數(shù)層次具備相同的公共行為時,采用 透明組合模式 也許會更好(代價:為剩下少數(shù)層次節(jié)點引入不需要的方法)株茶;而如果當(dāng)系統(tǒng)各個層次差異性行為較多或者樹節(jié)點層次相對穩(wěn)定(健壯)時来涨,采用 安全組合模式

:設(shè)計模式的出現(xiàn)并不是說我們要寫的代碼一定要遵循設(shè)計模式所要求的方方面面,這是不現(xiàn)實同時也是不可能的启盛。設(shè)計模式的出現(xiàn)蹦掐,其實只是強調(diào)好的代碼所具備的一些特征(六大設(shè)計原則),這些特征對于項目開發(fā)是具備積極效應(yīng)的僵闯,但不是說我們每實現(xiàn)一個類就一定要全部滿足設(shè)計模式的要求卧抗,如果真的存在完全滿足設(shè)計模式的要求,反而可能存在過度設(shè)計的嫌疑鳖粟。同時社裆,23種設(shè)計模式,其實都是嚴(yán)格依循設(shè)計模式六大原則進(jìn)行設(shè)計向图,只是不同的模式在不同的場景中會更加適用泳秀。設(shè)計模式的理解應(yīng)該重于意而不是形,真正編碼時榄攀,經(jīng)常使用的是某種設(shè)計模式的變形體嗜傅,真正切合項目的模式才是正確的模式。

舉個例子

例子:對于程序員來說檩赢,電腦是我們每天都要接觸的吕嘀。它的目錄系統(tǒng)其實就是一個典型的樹形結(jié)構(gòu),目錄包含文件夾和文件漠畜,文件夾里面又可以包含文件夾和文件···下面我們就用代碼來實現(xiàn)一個目錄系統(tǒng)币他。

分析:目錄系統(tǒng)有兩個大的層次:文件夾,文件憔狞。其中蝴悉,文件夾能容乃其他層次,為樹枝節(jié)點瘾敢;文件為最小單位拍冠,為葉子節(jié)點尿这。由于目錄系統(tǒng)層次較少,且樹枝節(jié)點(文件夾)結(jié)構(gòu)相對穩(wěn)定庆杜,而文件其實可以有很多類型射众,所以這里我們選擇使用 安全組合模式 來實現(xiàn)目錄系統(tǒng),可以避免為葉子類型(文件)引入冗余方法晃财。

代碼如下:

class Client {
    public static void main(String[] args) {
        Folder diskC = new Folder("C:\\");
        Folder windows = new Folder("---Windows");
        Folder system32 = new Folder("------system32");
        File calcFile = new File("---------calc.exe");
        File pingFile = new File("---------ping.exe");

        diskC.addDir(windows);
        windows.addDir(system32);
        system32.addDir(calcFile);
        system32.addDir(pingFile);

        diskC.print();

    }

    //抽象根類:Component
    static abstract class Directory {
        protected String name;

        public Directory(String name) {
            this.name = name;
        }

        public abstract void print();
    }

    //葉子節(jié)點:Leaf
    static class File extends Directory {

        public File(String name) {
            super(name);
        }

        @Override
        public void print() {
            System.out.println(this.name + ": file");
        }
    }

    //樹枝節(jié)點:Composite
    static class Folder extends Directory {
        private List<Directory> mDirs;

        public Folder(String name) {
            super(name);
            this.mDirs = new ArrayList<>();
        }

        @Override
        public void print() {
            System.out.println(this.name + ": Folder");
            for (Directory dir : this.mDirs) {
                dir.print();
            }
        }

        public boolean addDir(Directory dir) {
            return this.mDirs.add(dir);
        }

        public boolean removeDir(Directory dir) {
            return this.mDirs.remove(dir);
        }

        public Directory getDir(int index) {
            return this.mDirs.get(index);
        }
    }
}

客戶端創(chuàng)建了一個磁盤 C:\叨橱,并再 C 盤下創(chuàng)建了 windows 目錄,windows 目錄下又創(chuàng)建了 system32 目錄断盛,system32 目錄下包含兩個文件 calc.exe 和 ping.exe罗洗。上面代碼執(zhí)行的結(jié)果如下:

C:\: Folder
---Windows: Folder
------system32: Folder
---------calc.exe: File
---------ping.exe: File
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钢猛,隨后出現(xiàn)的幾起案子伙菜,更是在濱河造成了極大的恐慌,老刑警劉巖命迈,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贩绕,死亡現(xiàn)場離奇詭異,居然都是意外死亡壶愤,警方通過查閱死者的電腦和手機淑倾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來公你,“玉大人踊淳,你說我怎么就攤上這事假瞬∩驴浚” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵脱茉,是天一觀的道長剪芥。 經(jīng)常有香客問我,道長琴许,這世上最難降的妖魔是什么税肪? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮榜田,結(jié)果婚禮上益兄,老公的妹妹穿的比我還像新娘。我一直安慰自己箭券,他們只是感情好净捅,可當(dāng)我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辩块,像睡著了一般蛔六。 火紅的嫁衣襯著肌膚如雪荆永。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天国章,我揣著相機與錄音具钥,去河邊找鬼。 笑死液兽,一個胖子當(dāng)著我的面吹牛骂删,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播四啰,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼桃漾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拟逮?” 一聲冷哼從身側(cè)響起撬统,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎敦迄,沒想到半個月后恋追,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡罚屋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年苦囱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脾猛。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡撕彤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猛拴,到底是詐尸還是另有隱情羹铅,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布愉昆,位于F島的核電站职员,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏跛溉。R本人自食惡果不足惜焊切,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芳室。 院中可真熱鬧专肪,春花似錦、人聲如沸堪侯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抖格。三九已至诺苹,卻和暖如春咕晋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背收奔。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工掌呜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坪哄。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓质蕉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翩肌。 傳聞我的和親對象是個殘疾皇子模暗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內(nèi)容