設計模式--組合模式(Composite)

組合模式(Composite)

在現實生活中洒宝,存在很多“部分-整體”的關系雁歌,例如知残,大學中的部門與學院、總公司中的部門與分公司乏盐、學習用品中的書與書包制恍、生活用品中的衣月艮與衣柜以及廚房中的鍋碗瓢盆等。在軟件開發(fā)中也是這樣何吝,例如鹃唯,文件系統(tǒng)中的文件與文件夾坡慌、窗體程序中的簡單控件與容器控件等。對這些簡單對象與復合對象的處理跪者,如果用組合模式來實現會很方便熄求。

組合模式的定義與特點

  • 組合(Composite)模式的定義:又稱部分整體模式,是用于把一組相似的對象當作一個單一的對象柜蜈。組合模式依據樹形結構來組合對象指巡,用來表示部分以及整體層次隶垮。這種類型的設計模式屬于結構型模式狸吞,它創(chuàng)建了對象組的樹形結構指煎。這種模式創(chuàng)建了一個包含自己對象組的類便斥。該類提供了修改相同對象組的方式。
  • 組合(Composite)模式的優(yōu)點:1.高層模塊調用簡單像街。 2.節(jié)點自由增加晋渺。
  • 組合(Composite)模式的缺點:在使用組合模式時木西,其葉子和數值的聲明都是實現類,而不是接口吗讶,違反了依賴倒置原則叼丑。

組合模式的結構與實現

1.模式的結構
組合模式包含以下主要角色。

  1. 抽象構件(Component)角色:它的主要作用是為樹葉構件和樹枝構件聲明公共接口纵寝,并實現它們的默認行為星立。在透明式的組合模式中抽象構件還聲明訪問和管理子類的接口;在安全式的組合模式中不聲明訪問和管理子類的接口室奏,管理工作由樹枝構件完成劲装。
  2. 樹葉構件(Leaf)角色:是組合中的葉節(jié)點對象占业,它沒有子節(jié)點,用于實現抽象構件角色中 聲明的公共接口南蹂。
  3. 樹枝構件(Composite)角色:是組合中的分支節(jié)點對象念恍,它有子節(jié)點晚顷。它實現了抽象構件角色中聲明的接口该默,它的主要作用是存儲和管理子部件策彤,通常包含 Add()、Remove()叽赊、GetChild() 等方法必搞。

組合模式分為透明式的組合模式安全式的組合模式恕洲。

(1) 透明方式:在該方式中,由于抽象構件聲明了所有子類中的全部方法霜第,所以客戶端無須區(qū)別樹葉對象和樹枝對象泌类,對客戶端來說是透明的。但其缺點是:樹葉構件本來沒有 Add()弹砚、Remove() 及 GetChild() 方法枢希,卻要實現它們(空實現或拋異常)苞轿,這樣會帶來一些安全性問題。其結構圖如圖 1 所示瑟俭。

圖1 透明式的組合模式的結構圖

(2) 安全方式:在該方式中秀睛,將管理子構件的方法移到樹枝構件中蹂安,抽象構件和樹葉構件沒有對子對象的管理方法,這樣就避免了上一種方式的安全性問題畜号,但由于葉子和分支有不同的接口允瞧,客戶端在調用時要知道樹葉對象和樹枝對象的存在,所以失去了透明性痹升。其結構圖如圖 2 所示畦韭。
圖2 安全式的組合模式的結構圖

2.模式的實現
假如要訪問集合 c0={leaf1,{leaf2,leaf3}} 中的元素艺配,其對應的樹狀圖如圖 3 所示。
圖3 集合c0的樹狀圖

下面給出透明式的組合模式的實現代碼皮钠,與安全式的組合模式的實現代碼類似麦轰,只要對其做簡單修改就可以了砖织。

    package composite;
    import java.util.ArrayList;
    public class CompositePattern
    {
        public static void main(String[] args)
        {
            Component c0=new Composite(); 
            Component c1=new Composite(); 
            Component leaf1=new Leaf("1"); 
            Component leaf2=new Leaf("2"); 
            Component leaf3=new Leaf("3");          
            c0.add(leaf1); 
            c0.add(c1);
            c1.add(leaf2); 
            c1.add(leaf3);          
            c0.operation(); 
        }
    }
    //抽象構件
    interface Component
    {
        public void add(Component c);
        public void remove(Component c);
        public Component getChild(int i);
        public void operation();
    }
    //樹葉構件
    class Leaf implements Component
    {
        private String name;
        public Leaf(String name)
        {
            this.name=name;
        }
        public void add(Component c){ }           
        public void remove(Component c){ }   
        public Component getChild(int i)
        {
            return null;
        }   
        public void operation()
        {
            System.out.println("樹葉"+name+":被訪問镶苞!"); 
        }
    }
    //樹枝構件
    class Composite implements Component
    {
        private ArrayList<Component> children=new ArrayList<Component>();   
        public void add(Component c)
        {
            children.add(c);
        }   
        public void remove(Component c)
        {
            children.remove(c);
        }   
        public Component getChild(int i)
        {
            return children.get(i);
        }   
        public void operation()
        {
            for(Object obj:children)
            {
                ((Component)obj).operation();
            }
        }    
    }

程序運行結果如下:

樹葉1:被訪問茂蚓!
樹葉2:被訪問!
樹葉3:被訪問晾浴!

組合模式的實例

下面我們以公司的層級結構為例牍白,先看一下這個例子中該公司的層級結構(該例選自大話設計模式——程杰著)。

公司層級結構圖

這種部分與整體的關系切省,我們就可以考慮使用組合模式帕胆,下面采用組合模式的透明模式對其實現,UML圖如下:
URL圖

1. 具體公司類
此為樹枝節(jié)點芙盘,實現添加儒老、移除记餐、顯示和履行職責四種方法。

public class ConcreteCompany extends Company {

    private List<Company> companyList = new ArrayList<Company>();
    
    public ConcreteCompany(String name) {
        super(name);
    }

    @Override
    public void add(Company company) {
        this.companyList.add(company);
    }

    @Override
    public void remove(Company company) {
        this.companyList.remove(company);
    }

    @Override
    public void display(int depth) {
        //輸出樹形結構
        for(int i=0; i<depth; i++) {
            System.out.print('-');
        }
        System.out.println(name);
        
        //下級遍歷
        for (Company component : companyList) {
            component.display(depth + 1);
        }
    }

    @Override
    public void lineOfDuty() {
        //職責遍歷
        for (Company company : companyList) {
            company.lineOfDuty();
        }
    }

}

2. 人力資源部
葉子節(jié)點,add和remove方法空實現钠怯。

public class HRDepartment extends Company {

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

    @Override
    public void add(Company company) {
        
    }

    @Override
    public void remove(Company company) {
        
    }

    @Override
    public void display(int depth) {
        //輸出樹形結構的子節(jié)點
        for(int i=0; i<depth; i++) {
            System.out.print('-');
        }
        System.out.println(name);
    }

    @Override
    public void lineOfDuty() {
        System.out.println(name + " : 員工招聘培訓管理");
    }
    
}

3. 財務部
葉子節(jié)點晦炊,add和remove方法空實現。

public class FinanceDepartment extends Company {
    
    public FinanceDepartment(String name) {
        super(name);
    }

    @Override
    public void add(Company company) {
        
    }

    @Override
    public void remove(Company company) {
        
    }

    @Override
    public void display(int depth) {
        //輸出樹形結構的子節(jié)點
        for(int i=0; i<depth; i++) {
            System.out.print('-');
        }
        System.out.println(name);
    }

    @Override
    public void lineOfDuty() {
        System.out.println(name + " : 公司財務收支管理");
    }
    
}

4. Client客戶端

public class Client {

    public static void main(String[] args) {
        //總公司
        ConcreteCompany root = new ConcreteCompany("北京總公司");
        root.add(new HRDepartment("總公司人力資源部"));
        root.add(new FinanceDepartment("總公司財務部"));
        
        //分公司
        ConcreteCompany company = new ConcreteCompany("上海華東分公司");
        company.add(new HRDepartment("華東分公司人力資源部"));
        company.add(new FinanceDepartment("華東分公司財務部"));
        root.add(company);
        
        //辦事處
        ConcreteCompany company1 = new ConcreteCompany("南京辦事處");
        company1.add(new HRDepartment("南京辦事處人力資源部"));
        company1.add(new FinanceDepartment("南京辦事處財務部"));
        company.add(company1);
        
        ConcreteCompany company2 = new ConcreteCompany("杭州辦事處");
        company2.add(new HRDepartment("杭州辦事處人力資源部"));
        company2.add(new FinanceDepartment("杭州辦事處財務部"));
        company.add(company2);
        
        System.out.println("結構圖:");
        root.display(1);
        
        System.out.println("\n職責:");
        root.lineOfDuty();
    }
    
}

運行結果如下:


image

????????組合模式這樣就定義了包含人力資源部和財務部這些基本對象和分公司、辦事處等組合對象的類層次結構霞捡。
????????基本對象可以被組合成更復雜的組合對象薄疚,而這個組合對象又可以被組合街夭,這樣不斷地遞歸下去,客戶代碼中板丽,任何用到基本對象的地方都可以使用組合對象了。
????????這里用了透明模式猖辫,用戶不用關心到底是處理一個葉節(jié)點還是處理一個組合組件住册,也就用不著為定義組合而寫一些選擇判斷語句了瓮具。簡單點說就是組合模式可以讓客戶一致地使用組合結構和單個對象。

組合模式的應用場景

前面分析了組合模式的結構與特點叹阔,下面分析它適用的以下應用場景传睹。
1.在需要表示一個對象整體與部分的層次結構的場合欧啤。
2.要求對用戶隱藏組合對象與單個對象的不同,用戶可以用統(tǒng)一的接口使用組合結構中的所有對象的場合邢隧。

組合模式的擴展

如果對前面介紹的組合模式中的樹葉節(jié)點和樹枝節(jié)點進行抽象倒慧,也就是說樹葉節(jié)點和樹枝節(jié)點還有子節(jié)點,這時組合模式就擴展成復雜的組合模式了炫贤,如 Java AWT/Swing 中的簡單組件 JTextComponent 有子類 JTextField付秕、JTextArea,容器組件 Container 也有子類 Window俩垃、Panel汰寓。復雜的組合模式的結構圖如下圖所示有滑。

復雜的組合模式的結構圖

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市望艺,隨后出現的幾起案子找默,更是在濱河造成了極大的恐慌,老刑警劉巖惩激,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件风钻,死亡現場離奇詭異,居然都是意外死亡鸣个,警方通過查閱死者的電腦和手機布朦,發(fā)現死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門是趴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人做盅,你說我怎么就攤上這事窘哈」鐾瘢” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵远剩,是天一觀的道長骇窍。 經常有香客問我腹纳,道長驱犹,這世上最難降的妖魔是什么足画? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任淹辞,我火速辦了婚禮,結果婚禮上蔬将,老公的妹妹穿的比我還像新娘攻冷。我一直安慰自己遍希,他們只是感情好凿蒜,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著州泊,像睡著了一般漂洋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上演训,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天样悟,我揣著相機與錄音庭猩,去河邊找鬼。 笑死震糖,一個胖子當著我的面吹牛趴腋,可吹牛的內容都是我干的。 我是一名探鬼主播疏叨,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蚤蔓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了单寂?” 一聲冷哼從身側響起吐辙,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤昏苏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洼专,有當地人在樹林里發(fā)現了一具尸體孵构,經...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡颈墅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了官还。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叹俏。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡粘驰,死狀恐怖,靈堂內的尸體忽然破棺而出愕掏,到底是詐尸還是另有隱情顶伞,我是刑警寧澤剑梳,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布垢乙,位于F島的核電站语卤,受9級特大地震影響,放射性物質發(fā)生泄漏粹舵。R本人自食惡果不足惜钮孵,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望眼滤。 院中可真熱鬧巴席,春花似錦、人聲如沸诅需。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诱担。三九已至毡证,卻和暖如春电爹,著一層夾襖步出監(jiān)牢的瞬間蔫仙,已是汗流浹背丐箩。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工摇邦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屎勘。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓施籍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親概漱。 傳聞我的和親對象是個殘疾皇子丑慎,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內容