10.組合模式(結(jié)構(gòu)型)

組合模式(結(jié)構(gòu)型)

一递雀、概述

對于樹形結(jié)構(gòu)宽闲,當(dāng)容器對象(如文件夾)的某一個方法被調(diào)用時泪电,將遍歷整個樹形結(jié)構(gòu)秆乳,尋找也包含這個方法的成員對象(可以是容器對象懦鼠,也可以是葉子對象)并調(diào)用執(zhí)行,牽一而動百屹堰,其中使用了遞歸調(diào)用的機制來對整個結(jié)構(gòu)進(jìn)行處理葛闷。由于容器對象和葉子對象在功能上的區(qū)別,在使用這些對象的代碼中必須有區(qū)別地對待容器對象和葉子對象双藕,而實際上大多數(shù)情況下我們希望一致地處理它們淑趾,因為對于這些對象的區(qū)別對待將會使得程序非常復(fù)雜。組合模式為解決此類問題而誕生忧陪,它可以讓葉子對象和容器對象的使用具有一致性扣泊。

  1. 組合模式(Composite Pattern): 組合多個對象形成樹形結(jié)構(gòu)以表示具有“整體—部分”關(guān)系的層次結(jié)構(gòu)。組合模式對單個對象(即葉子對象)和組合對象(即容器對象)的使用具有一致性嘶摊,組合模式又可以稱為“整體—部分”(Part-Whole)模式延蟹,它是一種對象結(jié)構(gòu)型模式。

在組合模式中引入了抽象構(gòu)件類Component叶堆,它是所有容器類和葉子類的公共父類阱飘,客戶端針對Component進(jìn)行編程。

1). 成員角色


組合模式典型結(jié)構(gòu)圖.png
  1. Component(抽象構(gòu)件):它可以是接口或抽象類虱颗,為葉子構(gòu)件和容器構(gòu)件對象聲明接口沥匈,在該角色中可以包含所有子類共有行為的聲明和實現(xiàn)。在抽象構(gòu)件中定義了訪問及管理它的子構(gòu)件的方法忘渔,如增加子構(gòu)件高帖、刪除子構(gòu)件、獲取子構(gòu)件等畦粮。
  2. Leaf(葉子構(gòu)件):它在組合結(jié)構(gòu)中表示葉子節(jié)點對象散址,葉子節(jié)點沒有子節(jié)點,它實現(xiàn)了在抽象構(gòu)件中定義的行為宣赔。對于那些訪問及管理子構(gòu)件的方法预麸,可以通過異常等方式進(jìn)行處理。
  3. Composite(容器構(gòu)件):它在組合結(jié)構(gòu)中表示容器節(jié)點對象儒将,容器節(jié)點包含子節(jié)點吏祸,其子節(jié)點可以是葉子節(jié)點,也可以是容器節(jié)點椅棺,它提供一個集合用于存儲子節(jié)點犁罩,實現(xiàn)了在抽象構(gòu)件中定義的行為齐蔽,包括那些訪問及管理子構(gòu)件的方法,在其業(yè)務(wù)方法中可以遞歸調(diào)用其子節(jié)點的業(yè)務(wù)方法床估。

組合模式的關(guān)鍵是定義了一個抽象構(gòu)件類含滴,它既可以代表葉子,又可以代表容器丐巫,而客戶端針對該抽象構(gòu)件類進(jìn)行編程谈况,無須知道它到底表示的是葉子還是容器,可以對其進(jìn)行統(tǒng)一處理递胧。同時容器對象與抽象構(gòu)件類之間還建立一個聚合關(guān)聯(lián)關(guān)系碑韵,在容器對象中既可以包含葉子,也可以包含容器缎脾,以此實現(xiàn)遞歸組合祝闻,形成一個樹形結(jié)構(gòu)。

二遗菠、組合模式案例

使用組合模式進(jìn)行殺毒然間的框架設(shè)計联喘。

殺毒軟件組合模式框架設(shè)計.png

文件命名有些調(diào)整

1). Component 抽象構(gòu)件

/**
 * 抽象文件類:抽象構(gòu)件接口
 */
interface File {
    /**
     * 默認(rèn)方法
     * @param file
     */
    public default void add(File file) {
        throw new RuntimeException("reject");
    }
    public default void remove(File file) {
        throw new RuntimeException("reject");
    }
    public default File getChild(int index) {
        throw new RuntimeException("reject");
    }
    void killVirus();
}

2). Leaf 葉子構(gòu)件

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 圖像文件類:葉子構(gòu)建
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
class ImageFile implements File {

    private String name;

    @Override
    public void killVirus() {
        System.out.println("---對圖像文件:" + name + "正在殺毒!");
    }
}

/**
 * 文本文件類:葉子構(gòu)件
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
class TextFile implements File {
    private String name;

    @Override
    public void killVirus() {
        System.out.println("---對文本文件:" + name + "正在殺毒辙纬!");
    }
}

/**
 * 視頻文件類:葉子構(gòu)建
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
class VideoFile implements File {
    private String name;

    @Override
    public void killVirus() {
        System.out.println("---對視頻文件:" + name + "正在殺毒豁遭!");
    }
}

3). Composite 容器構(gòu)件

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

/**
 * 文件夾類:容器構(gòu)件
 */
class Folder implements File {

    /**
     * 存儲File類型的成員
     */
    private List<File> fileList = new ArrayList<>();
    private String name;

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

    @Override
    public void add(File file) {
        this.fileList.add(file);
    }

    @Override
    public void remove(File file) {
        this.fileList.remove(file);
    }

    @Override
    public File getChild(int index) {
        return this.fileList.get(index);
    }

    @Override
    public void killVirus() {
        System.out.println("---對文件夾:" + name + "進(jìn)行殺毒!");
        for (File file : fileList) {
            file.killVirus();
        }
    }
}

4). 測試程序

/**
 * 組合模式案例
 * @author Liucheng
 * @since 2019-07-26
 */
public class Client {

    public static void main(String[] args) {

        File folder1 = new Folder("Sunny的資料");
        File folder2 = new Folder("圖像文件");
        File folder3 = new Folder("文本文件");
        File folder4 = new Folder("視頻文件");

        File file1 = new ImageFile("小龍女.jpg");
        File file2 = new ImageFile("張無忌.gif");
        File file3 = new TextFile("九陰真經(jīng).txt");
        File file4 = new TextFile("葵花寶典.doc");
        File file5 = new VideoFile("笑傲江湖.rmvb");

        folder2.add(file1);
        folder2.add(file2);
        folder3.add(file3);
        folder3.add(file4);
        folder4.add(file5);
        folder1.add(folder2);
        folder1.add(folder3);
        folder1.add(folder4);

        //從“Sunny的資料”節(jié)點開始進(jìn)行殺毒操作
        folder1.killVirus();
    }
}

三贺拣、透明組合模式與安全組合模式

1). 透明組合模式

透明組合模式中蓖谢,抽象構(gòu)件Component中聲明了所有用于管理成員對象的方法,包括add()譬涡、remove()以及getChild()等方法闪幽,這樣做的好處是確保所有的構(gòu)件類都有相同的接口。在客戶端看來昂儒,葉子對象與容器對象所提供的方法是一致的沟使,客戶端可以相同地對待所有的對象委可。透明組合模式也是組合模式的標(biāo)準(zhǔn)形式渊跋,雖然上面的解決方案一在客戶端可以有不透明的實現(xiàn)方法,但是由于在抽象構(gòu)件中包含add()着倾、remove()等方法(還可以將這些方法定位抽象方法拾酝,在子類中實現(xiàn),拋異常)卡者,因此它還是透明組合模式蒿囤,透明組合模式的完整結(jié)構(gòu)如圖11-6所示:


透明組合模式.png

透明組合模式的缺點是不夠安全,因為葉子對象和容器對象在本質(zhì)上是有區(qū)別的崇决。葉子對象不可能有下一個層次的對象材诽,即不可能包含成員對象底挫,因此為其提供add()、remove()以及getChild()等方法是沒有意義的脸侥,這在編譯階段不會出錯建邓,但在運行階段如果調(diào)用這些方法可能會出錯(如果沒有提供相應(yīng)的錯誤處理代碼)。

2). 安全組合模式

安全組合模式中睁枕,在抽象構(gòu)件Component中沒有聲明任何用于管理成員對象的方法官边,而是在Composite(在接口中只定義公共的方法,子類的方法各自定義實現(xiàn))類中聲明并實現(xiàn)這些方法外遇。這種做法是安全的注簿,因為根本不向葉子對象提供這些管理成員對象的方法,對于葉子對象跳仿,客戶端不可能調(diào)用到這些方法诡渴,這就是解決方案二所采用的實現(xiàn)方式。安全組合模式的結(jié)構(gòu)如圖11-7所示:

安全組合模式.png

安全組合模式的缺點是不夠透明菲语,因為葉子構(gòu)件和容器構(gòu)件具有不同的方法玩徊,且容器構(gòu)件中那些用于管理成員對象的方法沒有在抽象構(gòu)件類中定義,因此客戶端不能完全針對抽象編程谨究,必須有區(qū)別地對待葉子構(gòu)件和容器構(gòu)件恩袱。在實際應(yīng)用中,安全組合模式的使用頻率也非常高胶哲,在Java AWT中使用的組合模式就是安全組合模式畔塔。

四、組合模式總結(jié)

組合模式使用面向?qū)ο蟮乃枷雭韺崿F(xiàn)樹形結(jié)構(gòu)的構(gòu)建與處理鸯屿,描述了如何將容器對象和葉子對象進(jìn)行遞歸組合澈吨,實現(xiàn)簡單,靈活性好寄摆。由于在軟件開發(fā)中存在大量的樹形結(jié)構(gòu)谅辣,因此組合模式是一種使用頻率較高的結(jié)構(gòu)型設(shè)計模式,在XML解析婶恼、組織結(jié)構(gòu)樹處理桑阶、文件系統(tǒng)設(shè)計等領(lǐng)域,組合模式都得到了廣泛應(yīng)用勾邦。

1). 優(yōu)點

  1. 組合模式可以清楚地定義分層次的復(fù)雜對象蚣录,表示對象的全部或部分層次,它讓客戶端忽略了層次的差異眷篇,方便對整個層次結(jié)構(gòu)進(jìn)行控制萎河。
  2. 客戶端可以一致地使用一個組合結(jié)構(gòu)或其中單個對象,不必關(guān)心處理的是單個對象還是整個組合結(jié)構(gòu),簡化了客戶端代碼虐杯。
  3. 在組合模式中增加新的容器構(gòu)件和葉子構(gòu)件都很方便玛歌,無須對現(xiàn)有類庫進(jìn)行任何修改,符合“開閉原則”擎椰。
  4. 組合模式為樹形結(jié)構(gòu)的面向?qū)ο髮崿F(xiàn)提供了一種靈活的解決方案沾鳄,通過葉子對象和容器對象的遞歸組合,可以形成復(fù)雜的樹形結(jié)構(gòu)确憨,但對樹形結(jié)構(gòu)的控制卻非常簡單译荞。

2). 缺點

在增加新構(gòu)件時很難對容器中的構(gòu)件類型進(jìn)行限制。有時候我們希望一個容器中只能有某些特定類型的對象休弃,例如在某個文件夾中只能包含文本文件吞歼,使用組合模式時,不能依賴類型系統(tǒng)來施加這些約束塔猾,因為它們都來自于相同的抽象層篙骡,在這種情況下,必須通過在運行時進(jìn)行類型檢查來實現(xiàn)丈甸,這個實現(xiàn)過程較為復(fù)雜糯俗。

3). 適用場景

  1. 在具有整體和部分的層次結(jié)構(gòu)中,希望通過一種方式忽略整體與部分的差異睦擂,客戶端可以一致地對待它們得湘。
  2. 在一個使用面向?qū)ο笳Z言開發(fā)的系統(tǒng)中需要處理一個樹形結(jié)構(gòu)。
  3. 在一個系統(tǒng)中能夠分離出葉子對象和容器對象顿仇,而且它們的類型不固定淘正,需要增加一些新的類型。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末臼闻,一起剝皮案震驚了整個濱河市鸿吆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌述呐,老刑警劉巖惩淳,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乓搬,居然都是意外死亡思犁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門缤谎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抒倚,“玉大人,你說我怎么就攤上這事坷澡。” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵频敛,是天一觀的道長项郊。 經(jīng)常有香客問我,道長斟赚,這世上最難降的妖魔是什么着降? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮拗军,結(jié)果婚禮上任洞,老公的妹妹穿的比我還像新娘。我一直安慰自己发侵,他們只是感情好交掏,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刃鳄,像睡著了一般盅弛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叔锐,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天挪鹏,我揣著相機與錄音,去河邊找鬼愉烙。 笑死讨盒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的步责。 我是一名探鬼主播催植,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼勺择!你這毒婦竟也來了创南?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤省核,失蹤者是張志新(化名)和其女友劉穎稿辙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體气忠,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡邻储,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了旧噪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吨娜。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖淘钟,靈堂內(nèi)的尸體忽然破棺而出宦赠,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布勾扭,位于F島的核電站毡琉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏妙色。R本人自食惡果不足惜桅滋,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望身辨。 院中可真熱鬧丐谋,春花似錦、人聲如沸煌珊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怪瓶。三九已至萧落,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間洗贰,已是汗流浹背找岖。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留敛滋,地道東北人许布。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像绎晃,于是被迫代替她去往敵國和親蜜唾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

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