組合模式

類圖:


精髓

  • 樹形結(jié)構(gòu)效五,可以表現(xiàn)出對(duì)象的層次結(jié)構(gòu),與部分-整體的關(guān)系
  • (精髓中的精髓)節(jié)點(diǎn)實(shí)現(xiàn)同一個(gè)公共接口瞒渠,使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性(如果組合對(duì)象和單個(gè)對(duì)象行為上沒什么區(qū)別(如文件夾和文件皮璧,行為就基本不一樣,如果只是一個(gè)純粹的樹狀結(jié)構(gòu)的任務(wù)執(zhí)行計(jì)劃遥巴,每個(gè)節(jié)點(diǎn)都是單個(gè)對(duì)象而已千康,只是執(zhí)行順序不同,就沒必要抽象公共接口了铲掐,判斷葉子節(jié)點(diǎn)就是判斷children字段是否為空)拾弃,抽象接口這一層也可以免去)....也正是因?yàn)樗泄?jié)點(diǎn)都屬于同一類,才能如此方便的構(gòu)造一個(gè)樹結(jié)構(gòu)出來摆霉。

tips:leaf與composite實(shí)現(xiàn)的共同接口盡量用抽象類實(shí)現(xiàn)豪椿,可以將一些具體類別不能實(shí)現(xiàn)的方法設(shè)置為默認(rèn)拋出異常(headfirst作者提倡以拋異常的方式處理奔坟,如果需要在調(diào)用處理時(shí)判斷節(jié)點(diǎn)類型,就與模式的抽象初衷違背了)

網(wǎng)上找的一個(gè)殺毒軟件殺毒例子

1.抽象leaf和組合對(duì)象的公共接口

abstract class AbstractFile {  
    public void add(AbstractFile file){
        System.out.println("對(duì)不起搭盾,不支持該方法咳秉!");  
    }  
    public abstract void remove(AbstractFile file){
        System.out.println("對(duì)不起,不支持該方法增蹭!");  
    }    
    public abstract AbstractFile getChild(int i){
        System.out.println("對(duì)不起滴某,不支持該方法!");  
    }   
    public abstract void killVirus(); //所有類型都會(huì)進(jìn)行殺毒 
}  

2.分別實(shí)現(xiàn)每一種節(jié)點(diǎn)

  • 圖片類型(<b>leaf</b>)
class ImageFile extends AbstractFile {  
    private String name;  
      
    public ImageFile(String name) {  
        this.name = name;  
    }  
      
    public void add(AbstractFile file) {  
       System.out.println("對(duì)不起滋迈,不支持該方法霎奢!");  
    }  
      
    public void remove(AbstractFile file) {  
        System.out.println("對(duì)不起,不支持該方法饼灿!");  
    }  
      
    public AbstractFile getChild(int i) {  
        System.out.println("對(duì)不起筷转,不支持該方法耙替!");  
        return null;  
    }  
      
    public void killVirus() {  
        //模擬殺毒  
        System.out.println("----對(duì)圖像文件'" + name + "'進(jìn)行殺毒");  
    }  
}  
  • 文本文檔(<b>leaf</b>)
class TextFile extends AbstractFile {  
    private String name;  
      
    public TextFile(String name) {  
        this.name = name;  
    }  
      
    public void add(AbstractFile file) {  
       System.out.println("對(duì)不起,不支持該方法!");  
    }  
      
    public void remove(AbstractFile file) {  
        System.out.println("對(duì)不起王悍,不支持該方法!");  
    }  
      
    public AbstractFile getChild(int i) {  
        System.out.println("對(duì)不起睛挚,不支持該方法靠汁!");  
        return null;  
    }  
      
    public void killVirus() {  
        //模擬殺毒  
        System.out.println("----對(duì)文本文件'" + name + "'進(jìn)行殺毒");  
    }  
}  
  • 視頻(<b>leaf</b>)
class VideoFile extends AbstractFile {  
    private String name;  
      
    public VideoFile(String name) {  
        this.name = name;  
    }  
      
    public void add(AbstractFile file) {  
       System.out.println("對(duì)不起,不支持該方法皆疹!");  
    }  
      
    public void remove(AbstractFile file) {  
        System.out.println("對(duì)不起疏橄,不支持該方法!");  
    }  
      
    public AbstractFile getChild(int i) {  
        System.out.println("對(duì)不起略就,不支持該方法捎迫!");  
        return null;  
    }  
      
    public void killVirus() {  
        //模擬殺毒  
        System.out.println("----對(duì)視頻文件'" + name + "'進(jìn)行殺毒");  
    }  
}  
  • 文件夾(<b>composite</b>)
class Folder extends AbstractFile {  
    //定義集合fileList,用于存儲(chǔ)AbstractFile類型的成員  
    private ArrayList<AbstractFile> fileList=new ArrayList<AbstractFile>();  
    private String name;  
          
    public Folder(String name) {  
        this.name = name;  
    }  
      
    public void add(AbstractFile file) {  
       fileList.add(file);    
    }  
      
    public void remove(AbstractFile file) {  
        fileList.remove(file);  
    }  
      
    public AbstractFile getChild(int i) {  
        return fileList.get(i);  
    }  
      
    public void killVirus() {   //關(guān)鍵代碼表牢。窄绒。。
        System.out.println("****對(duì)文件夾'" + name + "'進(jìn)行殺毒");  //模擬殺毒  
          
        //遞歸調(diào)用成員構(gòu)件的killVirus()方法  
        for(AbstractFile obj : fileList) {  
            obj.killVirus();  
        }  
    }  
}  

3.客戶端測(cè)試類

class Client {  
    public static void main(String args[]) {  
        //針對(duì)抽象構(gòu)件編程  
        AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;  
          
        folder1 = new Folder("Sunny的資料");  
        folder2 = new Folder("圖像文件");  
        folder3 = new Folder("文本文件");  
        folder4 = new Folder("視頻文件");  
          
        file1 = new ImageFile("小龍女.jpg");  
        file2 = new ImageFile("張無忌.gif");  
        file3 = new TextFile("九陰真經(jīng).txt");  
        file4 = new TextFile("葵花寶典.doc");  
        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é)點(diǎn)開始進(jìn)行殺毒操作  
        folder1.killVirus();  
    }  
}  

/*
刪除出結(jié)果:
****對(duì)文件夾'Sunny的資料'進(jìn)行殺毒
****對(duì)文件夾'圖像文件'進(jìn)行殺毒
----對(duì)圖像文件'小龍女.jpg'進(jìn)行殺毒
----對(duì)圖像文件'張無忌.gif'進(jìn)行殺毒
****對(duì)文件夾'文本文件'進(jìn)行殺毒
----對(duì)文本文件'九陰真經(jīng).txt'進(jìn)行殺毒
----對(duì)文本文件'葵花寶典.doc'進(jìn)行殺毒
****對(duì)文件夾'視頻文件'進(jìn)行殺毒
----對(duì)視頻文件'笑傲江湖.rmvb'進(jìn)行殺毒


*/

4.如果不使用組合模式怎么實(shí)現(xiàn)

文件夾:

//文件夾類  
class Folder {   //沒有實(shí)現(xiàn)共同的接口
    private String name;  
    /*
        需要定義各種類型的子節(jié)點(diǎn)的存儲(chǔ)崔兴,區(qū)別對(duì)待
    */
    //定義集合folderList彰导,用于存儲(chǔ)Folder類型的成員  
    private ArrayList<Folder> folderList = new ArrayList<Folder>();  
    //定義集合imageList,用于存儲(chǔ)ImageFile類型的成員  
    private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>();  
    //定義集合textList敲茄,用于存儲(chǔ)TextFile類型的成員  
    private ArrayList<TextFile> textList = new ArrayList<TextFile>();  
      
    public Folder(String name) {  
        this.name = name;  
    }  
    /*
        以下的所有操作都必須區(qū)別對(duì)待
    */  
    //增加新的Folder類型的成員  
    public void addFolder(Folder f) {  
        folderList.add(f);  
    }  
      
    //增加新的ImageFile類型的成員  
    public void addImageFile(ImageFile image) {  
        imageList.add(image);  
    }  
      
    //增加新的TextFile類型的成員  
    public void addTextFile(TextFile text) {  
        textList.add(text);  
    }  
          
    //需提供三個(gè)不同的方法removeFolder()位谋、removeImageFile()和removeTextFile()來刪除成員,代碼省略  
  
    //需提供三個(gè)不同的方法getChildFolder(int i)折汞、getChildImageFile(int i)和getChildTextFile(int i)來獲取成員倔幼,代碼省略  
  
    /*
      每一種類型都要單獨(dú)去調(diào)用殺毒
    */
    public void killVirus() {  
        System.out.println("****對(duì)文件夾'" + name + "'進(jìn)行殺毒");  //模擬殺毒  
          
        //如果是Folder類型的成員,遞歸調(diào)用Folder的killVirus()方法  
        for(Folder obj : folderList) {  
            obj.killVirus();  
        }  
          
        //如果是ImageFile類型的成員爽待,調(diào)用ImageFile的killVirus()方法  
        for(ImageFile obj : imageList) {  
            obj.killVirus();  
        }  
          
        //如果是TextFile類型的成員损同,調(diào)用TextFile的killVirus()方法  
        for(TextFile obj : textList) {  
            obj.killVirus();  
        }  
    }   
}  

<b>當(dāng)然其他的原子節(jié)點(diǎn)也沒有實(shí)現(xiàn)公共接口</b>
下面看客戶端調(diào)用:

class Client {  
    public static void main(String args[]) {  
        Folder folder1,folder2,folder3;  
        folder1 = new Folder("Sunny的資料");  
        folder2 = new Folder("圖像文件");  
        folder3 = new Folder("文本文件");  
          
        ImageFile image1,image2;  
        image1 = new ImageFile("小龍女.jpg");  
        image2 = new ImageFile("張無忌.gif");  
          
        TextFile text1,text2;  
        text1 = new TextFile("九陰真經(jīng).txt");  
        text2 = new TextFile("葵花寶典.doc");  
          
        folder2.addImageFile(image1);  
        folder2.addImageFile(image2);  
        folder3.addTextFile(text1);  
        folder3.addTextFile(text2);  
        folder1.addFolder(folder2);  
        folder1.addFolder(folder3);  
          
        folder1.killVirus();  
    }  
}  

1.文件夾類Folder的設(shè)計(jì)和實(shí)現(xiàn)都非常復(fù)雜翩腐,需要定義多個(gè)集合存儲(chǔ)不同類型的成員,而且需要針對(duì)不同的成員提供增加膏燃、刪除和獲取等管理和訪問成員的方法茂卦,存在大量的冗余代碼,系統(tǒng)維護(hù)較為困難组哩;
2.由于系統(tǒng)沒有提供抽象層等龙,客戶端代碼必須有區(qū)別地對(duì)待充當(dāng)容器的文件夾Folder和充當(dāng)葉子的ImageFile和TextFile,無法統(tǒng)一對(duì)它們進(jìn)行處理伶贰;客戶端要構(gòu)造這個(gè)樹特別復(fù)雜蛛砰,只有folde類型才有add接口
3.系統(tǒng)的靈活性和可擴(kuò)展性差,如果需要增加新的類型的葉子和容器都需要對(duì)原有代碼進(jìn)行修改黍衙,例如如果需要在系統(tǒng)中增加一種新類型的視頻文件VideoFile泥畅,則必須修改Folder類的源代碼,否則無法在文件夾中添加視頻文件琅翻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末位仁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子方椎,更是在濱河造成了極大的恐慌聂抢,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棠众,死亡現(xiàn)場(chǎng)離奇詭異琳疏,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)摄欲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門轿亮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疮薇,“玉大人胸墙,你說我怎么就攤上這事“粗洌” “怎么了迟隅?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)励七。 經(jīng)常有香客問我智袭,道長(zhǎng),這世上最難降的妖魔是什么掠抬? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任吼野,我火速辦了婚禮,結(jié)果婚禮上两波,老公的妹妹穿的比我還像新娘瞳步。我一直安慰自己闷哆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布单起。 她就那樣靜靜地躺著抱怔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嘀倒。 梳的紋絲不亂的頭發(fā)上屈留,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音测蘑,去河邊找鬼灌危。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碳胳,可吹牛的內(nèi)容都是我干的乍狐。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼固逗,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼浅蚪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烫罩,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤惜傲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后贝攒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盗誊,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年隘弊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哈踱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梨熙,死狀恐怖开镣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咽扇,我是刑警寧澤邪财,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站质欲,受9級(jí)特大地震影響树埠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嘶伟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一怎憋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧九昧,春花似錦绊袋、人聲如沸赠橙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽期揪。三九已至,卻和暖如春规个,著一層夾襖步出監(jiān)牢的瞬間凤薛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工诞仓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缤苫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓墅拭,卻偏偏與公主長(zhǎng)得像活玲,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谍婉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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

  • 1 場(chǎng)景問題# 1.1 商品類別樹## 考慮這樣一個(gè)實(shí)際的應(yīng)用:管理商品類別樹舒憾。 在實(shí)現(xiàn)跟商品有關(guān)的應(yīng)用系統(tǒng)的時(shí)候...
    七寸知架構(gòu)閱讀 5,999評(píng)論 10 59
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)穗熬,斷路器镀迂,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 繼承是is-a的關(guān)系。組合和聚合有點(diǎn)像唤蔗,有些書上沒有作區(qū)分探遵,都稱之為has-a,有些書上對(duì)其進(jìn)行了較為嚴(yán)格區(qū)分妓柜,組...
    時(shí)待吾閱讀 453評(píng)論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法箱季,類相關(guān)的語法,內(nèi)部類的語法棍掐,繼承相關(guān)的語法藏雏,異常的語法,線程的語...
    子非魚_t_閱讀 31,598評(píng)論 18 399
  • 體驗(yàn)環(huán)境 版本:V 5.7.1機(jī)型:iPhone 4s(iOS 8.4)時(shí)間:2015.7.23 簡(jiǎn)述 來往這個(gè)應(yīng)...
    Hawe閱讀 558評(píng)論 0 3