【設(shè)計模式筆記】(十七)- 組合模式

一毕莱、簡述

組合模式(Composite Pattern),也稱作部分整體模式(Part-Whole Pattern)卜范,將一組相似的對象看做一個對象處理隧土,并根據(jù)一個樹狀結(jié)構(gòu)來組合對象;對象都提供一個統(tǒng)一的方法去訪問相應(yīng)的對象來處理多個對象的同一性問題藕甩。

組合模式屬于結(jié)構(gòu)設(shè)計模式之一施敢,而其設(shè)計目的就是將對象組合成樹形結(jié)構(gòu)以表示"部分-整體"的層次結(jié)構(gòu)周荐,使得用戶對單個對象和組合對象的使用具有一致性;所以決定組合模式的設(shè)計基礎(chǔ)就是樹狀結(jié)構(gòu)僵娃,組合模式所適用的情況也就是樹狀結(jié)構(gòu)或者適合適用樹狀結(jié)構(gòu)來解決問題的情況概作。

組合模式.png
  • Component:抽象節(jié)點(diǎn),為組合中的對象聲明統(tǒng)一接口
  • Composite:可以儲存子節(jié)點(diǎn)的節(jié)點(diǎn)對象默怨,并實(shí)現(xiàn)抽象節(jié)點(diǎn)的有關(guān)操作
  • Leaf:葉子節(jié)點(diǎn)讯榕,沒有子節(jié)點(diǎn)對象
  • Client:組合節(jié)點(diǎn)對象,進(jìn)行操作

這里所描述的是透明的組合模式匙睹,可以看到Component類中除了統(tǒng)一的操作方法doSomthing()方法以外愚屁,還有操作子節(jié)點(diǎn)的相關(guān)方法,而葉子節(jié)點(diǎn)Leaf類定義就是沒有葉子節(jié)點(diǎn)的痕檬,顯然這些操作子節(jié)點(diǎn)的方法就是多余的霎槐。

如果要讓Leaf類不繼承這些方法,只能將Component類中的這些方法放到它的子類Composite中梦谜;然而這樣的設(shè)計方式與依賴倒置原則相違背丘跌,所以這里并沒有采用這種組合模式,即安全的組合模式唁桩。

二闭树、實(shí)現(xiàn)

說到組合模式,最適合的就是文件系統(tǒng)的結(jié)構(gòu)了荒澡,文件夾中就子文件夾和文件报辱,子文件夾中可能又是如此,典型的樹狀結(jié)構(gòu)仰猖。

抽象的目錄類捏肢,有目錄名,有輸出目錄名饥侵,并提供添加目錄鸵赫、刪除目錄以及清空目錄的的功能方法

public abstract class Directory {
    //當(dāng)前目錄名
    private final String name;

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

    /**
     * 輸出目錄結(jié)構(gòu)
     */
    public abstract void print();

    /**
     * 添加一個文件或者文件夾
     * @param dir
     */
    public abstract void addDir(Directory dir);

    /**
     * 刪除一個文件或者文件夾
     * @param dir
     */
    public abstract void removeDir(Directory dir);

    /**
     * 清空目錄
     */
    public abstract void clear();

    /**
     * 獲取目錄中的所有目錄
     * @return
     */
    public abstract List<Directory> getDirectories();

    /**
     * 獲取目錄名
     * @return
     */
    public String getName() {
        return name;
    }
}

文件夾類,申明一個集合儲存自身所報的目錄躏升,實(shí)現(xiàn)了具體的目錄操作方法辩棒,在print()方法中循環(huán)調(diào)用集合中目錄的print()方法輸出

public class Folder extends Directory {

    /**
     * 當(dāng)前文件夾下的所有目錄元素
     */
    protected List<Directory> directories = new ArrayList<>();

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

    @Override
    public void print() {
        System.out.print(getName() + "[");
        Iterator<Directory> iterator = directories.iterator();
        while (iterator.hasNext()){
            Directory directory = iterator.next();
            directory.print();
            if(iterator.hasNext()){
                System.out.print(", ");
            }
        }
        System.out.print("]");
    }

    @Override
    public void addDir(Directory dir) {
        directories.add(dir);
    }

    @Override
    public void removeDir(Directory dir) {
        directories.remove(dir);
    }

    @Override
    public void clear() {
        directories.clear();
    }

    @Override
    public List<Directory> getDirectories() {
        return directories;
    }
}

文件類,實(shí)現(xiàn)了print()方法膨疏,由于沒有子目錄一睁,相關(guān)操作的方法都拋出異常

public class File extends Directory {

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

    @Override
    public void print() {
        System.out.print(getName());
    }

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

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

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

    @Override
    public List<Directory> getDirectories() {
        throw new UnsupportedOperationException("文件對象不支持該操作");
    }
}

測試代碼,模擬輸出C盤的結(jié)構(gòu)

public class Client {
    public static void main(String[] args){
        Directory root = new Folder("C");

        root.addDir(new Folder("windows"));

        Directory program = new Folder("Program File(x86)");
        program.addDir(new Folder("Intellij"));
        program.addDir(new File("cache"));
        root.addDir(program);

        root.addDir(new Folder("windows"));

        root.addDir(new File("log.txt"));
        root.addDir(new File("null.txt"));

        root.print();
    }
}

輸出結(jié)果:
C[windows[], Program File(x86)[Intellij[], cache], windows[], log.txt, null.txt]

三佃却、在Android中的實(shí)現(xiàn)

組合模式AndroidViewViewGroup的嵌套組合使用中得到了很好地展現(xiàn)者吁。采用的事安全的組合模式

View&ViewGroup.png

省略了ViewViewGroup的很多方法,在Android中饲帅,只有ViewGroup才能放ViewViewGroup也是View)复凳,View并不是容器所以ViewGroup相對于View多了幾個操作視圖的方法瘤泪。

看看ViewGroup的聲明,大概就明白和View有什么不同了育八,ViewGroup繼承于View同時還是實(shí)現(xiàn)了ViewParentViewManager兩個接口

public abstract class ViewGroup extends View implements ViewParent, ViewManager

先看看ViewManager接口对途,為ViewGroup提供了管理addViewupdateViewLayoutremoveView方法操作子View

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和一些焦點(diǎn)事件的處理方法髓棋,高版本的API中還添加了嵌套滑動的一些方法

ViewGroup除了所實(shí)現(xiàn)的這兩個接口與View不同以外实檀,還有重要的一點(diǎn),ViewGroup是抽象類按声,將ViewonLayout重置為抽象方法膳犹,也就是ViewGroup的子類必須實(shí)現(xiàn)onLayout方法來布局子ViewViewonLayout方法是空實(shí)現(xiàn)签则,因?yàn)閷τ谝粋€普通的View來說該方法并沒有什么實(shí)現(xiàn)價值镣奋。

除此之外,在View中比較重的兩個方法onMeasureonDrawViewGroup中都沒有被重寫怀愧,相對于onMeasure方法,在ViewGroup中增加了一些計算子View的方法余赢,如measureChildren芯义,measureChildrenWithManager等;而對于onDraw方法妻柒,VIewGroup定義了一個dispatchDraw方法來調(diào)其每一個子ViewonDraw方法扛拨。這樣ViewGroup就是真的像一個容器一樣,器職責(zé)只是負(fù)責(zé)對子元素的操作而非具體行為举塔。

四绑警、總結(jié)

組合模式解釋器模式有一定類同,都涉及遞歸的調(diào)用央渣,但是組合模式所提供的屬性層次結(jié)構(gòu)使得可以同等對待單個對象和對象集合计盒。不過是以犧牲單一原則換來的,而組合模式是通過繼承來實(shí)現(xiàn)的芽丹,這樣有缺少些的了擴(kuò)展性北启。

優(yōu)點(diǎn):

  • 清楚的定義層次,同時可以忽略層次差異拔第,方便對層次結(jié)構(gòu)進(jìn)行控制
  • 高層模塊可以一致的使用一個組合結(jié)構(gòu)或者其中單個對象咕村,不必關(guān)心處理的單個對象還是整個組合結(jié)構(gòu),簡化代碼
  • 對于枝干構(gòu)件和葉子構(gòu)件的新增很方便
  • 通過枝干對象和葉子對象的遞歸組合蚊俺,可以形成復(fù)雜的樹形結(jié)構(gòu)懈涛,同時保持簡單的方式進(jìn)行控制

缺點(diǎn):

  • 對于增加新構(gòu)件是很對容器中的構(gòu)件類型進(jìn)行限制。不能依賴類型系統(tǒng)來施加這些約束泳猬,因?yàn)樗鼈兌紒碜杂谙嗤某橄髮优疲谶@種情況下宇植,必須通過在運(yùn)行時進(jìn)行類型檢查來實(shí)現(xiàn),這個實(shí)現(xiàn)過程較為復(fù)雜价匠。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末当纱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子踩窖,更是在濱河造成了極大的恐慌坡氯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洋腮,死亡現(xiàn)場離奇詭異箫柳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)啥供,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門悯恍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人伙狐,你說我怎么就攤上這事涮毫。” “怎么了贷屎?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵罢防,是天一觀的道長。 經(jīng)常有香客問我唉侄,道長咒吐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任属划,我火速辦了婚禮恬叹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘同眯。我一直安慰自己绽昼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布须蜗。 她就那樣靜靜地躺著绪励,像睡著了一般。 火紅的嫁衣襯著肌膚如雪唠粥。 梳的紋絲不亂的頭發(fā)上疏魏,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音晤愧,去河邊找鬼大莫。 笑死,一個胖子當(dāng)著我的面吹牛官份,可吹牛的內(nèi)容都是我干的只厘。 我是一名探鬼主播烙丛,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼羔味!你這毒婦竟也來了河咽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤赋元,失蹤者是張志新(化名)和其女友劉穎忘蟹,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搁凸,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡媚值,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了护糖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褥芒。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嫡良,靈堂內(nèi)的尸體忽然破棺而出锰扶,到底是詐尸還是另有隱情,我是刑警寧澤寝受,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布少辣,位于F島的核電站,受9級特大地震影響羡蛾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锨亏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一痴怨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧器予,春花似錦浪藻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至反浓,卻和暖如春萌丈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雷则。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工辆雾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人月劈。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓度迂,卻偏偏與公主長得像藤乙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惭墓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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