設(shè)計模式-組合模式

組合模式介紹

組合模式(Composite Pattern)也稱為部分整體模式(Part-Whole Pattern),是結(jié)構(gòu)型設(shè)計模式之一酱固,它將一組相似的對象看做一個對象處理,并根據(jù)一個樹狀結(jié)構(gòu)來組合對象隅俘,然后提供統(tǒng)一的方法去訪問相應(yīng)的對象曼玩,以此忽略掉對象與對象之間的差別烘贴。

組合模式的定義

將對象組合成樹形結(jié)構(gòu)以表示 “部分-整體” 的層次結(jié)構(gòu)羡儿,使得用戶對單個對象和組合對象的使用具有一致性事示。

組合模式的使用場景

表示部分早像、整體層次結(jié)構(gòu)時,如樹形菜單肖爵,文件卢鹦、文件夾的管理。

組合模式的 UML 類圖

角色介紹:

  • Component:抽象根節(jié)點劝堪,定義系統(tǒng)各層次對象的共有方法和屬性冀自,可以預(yù)先定義一些默認(rèn)行為和屬性揉稚。
  • Composite:樹枝節(jié)點,定義樹枝節(jié)點的行為熬粗,存儲子節(jié)點搀玖,組合樹枝節(jié)點和葉子節(jié)點形成一個樹形結(jié)構(gòu);
  • Leaf:葉子節(jié)點驻呐,葉子節(jié)點對象灌诅,其下再無節(jié)點,是系統(tǒng)層次遍歷的最小單位含末。

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

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

    把所有公共方法都定義在 Component 中,這樣做的好處是客戶端無需分辨是葉子節(jié)點(Leaf)和樹枝節(jié)點(Composite)沼撕,它們具備完全一致的接口宋雏;缺點是葉子節(jié)點(Leaf)會繼承得到一些它所不需要(管理子類操作的方法)的方法,這與設(shè)計模式接口隔離原則相違背务豺。
  2. 安全組合模式:統(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è)計模式 依賴倒置原則舞骆。

組合模式的實現(xiàn)

這里以文件管理器中的文件和文件夾的層級結(jié)構(gòu)為例

透明組合模式

抽象根節(jié)點 Component

public abstract class Dir {
    private String name;

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

    public abstract void addDir(Dir dir);

    public abstract void removeDir(Dir dir);
    // 打印目錄結(jié)構(gòu)
    public abstract void print(int depth);

    public String getName() {
        return name;
    }
}

樹枝節(jié)點 Composite
對應(yīng)的就是文件夾

public class Folder extends Dir {
    private List<Dir> dirs = new ArrayList<>();

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

    @Override
    public void addDir(Dir dir) {
        dirs.add(dir);
    }

    @Override
    public void removeDir(Dir dir) {
        dirs.remove(dir);
    }

    @Override
    public void print(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("--");
        }
        System.out.println(getName());
        for (Dir dir : dirs) {
            dir.print(depth + 1);
        }
    }
}

葉子節(jié)點 Leaf
也就是文件類

public class File extends Dir {
    public File(String name) {
        super(name);
    }

    @Override
    public void addDir(Dir dir) {
        throw new UnsupportedOperationException("文件對象不支持該操作");
    }

    @Override
    public void removeDir(Dir dir) {
        throw new UnsupportedOperationException("文件對象不支持該操作");
    }

    @Override
    public void print(int depth) {
        for (int i = 0; i < depth; i++) {
            System.out.print("--");
        }
        System.out.println(getName());
    }

}

文件類不包含添加和刪除的操作钥弯,故拋出異常,這里就違背了接口隔離原則督禽。
客戶端

public class Client {
    public static void main(String[] args) {
        Dir root = new Folder("/");
        Dir file = new File("root.txt");
        root.addDir(file);
        root.removeDir(file);

        Dir folder1 = new Folder("home");
        Dir file1 = new File("home.txt");
        folder1.addDir(file1);

        Dir folder2 = new Folder("etc");
        Dir file2 = new File("etc.conf");
        folder2.addDir(file2);

        root.addDir(folder1);
        root.addDir(folder2);

        root.print(0);
    }
}

客戶端聲明類型均采用抽象類型脆霎,符合依賴倒置原則。
輸出結(jié)果:

/
--home
----home.txt
--etc
----etc.conf

安全組合模式

將添加狈惫,刪除操作只存在樹枝節(jié)點中睛蛛,就變?yōu)榘踩M合模式。葉子節(jié)點就無需重寫自己不需要的方法,符合接口隔離原則玖院,此時客戶端要創(chuàng)建樹枝節(jié)點菠红,只能聲明為 Folder 類型,違背了依賴導(dǎo)致原則难菌。

問:透明組合模式 和 安全組合模式 都有各自的優(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)定(健壯)時,采用 安全組合模式澜汤。

總結(jié)

優(yōu)點
1.可以清晰地定義分層次的復(fù)雜對象蚜迅。
2.增加節(jié)點方便。
缺點
1.透明組合模式 和 安全組合模式俊抵,各自優(yōu)缺點比較明顯谁不,需要根據(jù)實際情況進(jìn)行選擇。

Android 源碼中組合模式

Android 源碼中有一個非常經(jīng)典的組合模式實現(xiàn)徽诲,那就是 ViewViewGroup 的組合刹帕,如下圖。


ViewGroup 就是容器谎替,相當(dāng)于樹枝節(jié)點偷溺,其可以包含 TextView,Button等,也可以包含繼承自 ViewGroup 的節(jié)點钱贯,但對于葉子節(jié)點 TextView 等就無法包含 ViewGroup挫掏。將添加、移除子節(jié)點的操作都定義在了 ViewGroup 中喷舀,故該組合模式為安全的組合模式砍濒。

為什么 ViewGroup 有容器的功能

ViewGroup 是繼承自 View 類的。

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    ...
}

ViewGroup 與 View 差別就在于 ViewGroup 實現(xiàn)了 ViewParent 和 ViewManager接口硫麻。
ViewManager 定義了 addView爸邢、removeView 等對子視圖操作的方法

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

ViewParent 則定義了刷新容器的接口 requestLayout 和其他一些焦點事件處理的接口

public interface ViewParent {
  // 請求重新布局
  public void requestLayout();
  // 獲取父 View(不是父類)
  public ViewParent getParent();
  // 請求子視圖的焦點
  public void requestChildFocus(View child, View focused);
  ...
}

ViewGroup 實現(xiàn)的這兩個接口與 View 不一樣,還有重要一點就是 ViewGroup 是抽象類拿愧,其將 View 中的 onLayout 方法重置為抽象方法杠河,也就是容器子類必須實現(xiàn),View 中該方法是空實現(xiàn),因為對應(yīng)一個普通 View 來說該方法沒有什么實現(xiàn)價值券敌。View 測繪流程的 onMeasureonDraw 在 ViewGroup 都沒有重寫唾戚,對于 onMeasure 方法,在ViewGroup 中增加了測量子View的方法待诅,如 measureChildren叹坦;而對于 onDraw 方法,ViewGroup 中定義了 dispatchDraw 方法來調(diào)用每一個子 View 的 onDraw 方法卑雁。由此可見募书,ViewGroup 就是一個容器,只負(fù)責(zé)對子元素的操作测蹲,而非具體的個體行為莹捡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市扣甲,隨后出現(xiàn)的幾起案子篮赢,更是在濱河造成了極大的恐慌,老刑警劉巖琉挖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件启泣,死亡現(xiàn)場離奇詭異,居然都是意外死亡粹排,警方通過查閱死者的電腦和手機种远,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顽耳,“玉大人坠敷,你說我怎么就攤上這事∩涓唬” “怎么了膝迎?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胰耗。 經(jīng)常有香客問我限次,道長,這世上最難降的妖魔是什么柴灯? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任卖漫,我火速辦了婚禮,結(jié)果婚禮上赠群,老公的妹妹穿的比我還像新娘羊始。我一直安慰自己,他們只是感情好查描,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布突委。 她就那樣靜靜地躺著柏卤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匀油。 梳的紋絲不亂的頭發(fā)上缘缚,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音敌蚜,去河邊找鬼桥滨。 笑死,一個胖子當(dāng)著我的面吹牛弛车,可吹牛的內(nèi)容都是我干的该园。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼帅韧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了啃勉?” 一聲冷哼從身側(cè)響起忽舟,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淮阐,沒想到半個月后叮阅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡泣特,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年浩姥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片状您。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡勒叠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出膏孟,到底是詐尸還是另有隱情眯分,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布柒桑,位于F島的核電站弊决,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏魁淳。R本人自食惡果不足惜飘诗,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望界逛。 院中可真熱鬧昆稿,春花似錦、人聲如沸仇奶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至岛抄,卻和暖如春别惦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背夫椭。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工掸掸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹭秋。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓扰付,卻偏偏與公主長得像,于是被迫代替她去往敵國和親仁讨。 傳聞我的和親對象是個殘疾皇子羽莺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

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

  • 前言 Android的設(shè)計模式系列文章介紹,歡迎關(guān)注洞豁,持續(xù)更新中: Android的設(shè)計模式-設(shè)計模式的六大原則一...
    四月葡萄閱讀 4,636評論 1 14
  • 基本介紹 定義 將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)眯娱,使得用戶對單個對象和組合對象的使用具有一致性情组。...
    敏捷Studio閱讀 527評論 0 0
  • 本文的主要內(nèi)容: 介紹組合模式 示例 組合模式總結(jié) 源碼分析組合模式的典型應(yīng)用java.awt中的組合模式Java...
    小旋鋒的簡書閱讀 1,030評論 0 4
  • 將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)庸队,使得用戶對單個對象和組合對象的使用具有一致性软瞎,以便能夠讓客戶端...
    Ant_way閱讀 471評論 0 0
  • 今日奇跡190120 1、早晨睡到自然醒曙咽,全身有活力蛔趴,心底有無限的感恩,一睜眼看見陽光灑滿房間例朱,好開心的笑了孝情。 2...
    千子蓮閱讀 346評論 0 1