我學設計模式 之 合成模式

sz_bdqn發表於2010-09-26

我學設計模式之合成模式

 

1.  簡介

合成模式屬於物件的結構模式,有時又叫做 部分 整體模式。合成模式將物件組織到樹結構中,可以用來描述整體與部分的關係。

        合成模式把部分和整體的關係用樹結構表示出來。合成模式使得客戶端把一個個單獨的成分物件和有他們複合而成的合成物件同等看待。

 

 

2.  組合模式的結構

先給出合成模式的簡單類圖如下:

 

 

       從類圖中可以看出合成模式涉及3個角色:

抽象構件角色(Component)這是一個抽象角色,他給參加組合的物件規定一個介面,這個角色給出共有的介面及其預設行為。

       樹葉構件角色(Leaf)代表參加組合的樹葉物件。一個樹葉沒有下級的子物件。定義出參加原始物件的行為。

  樹枝構件角色(Composite):代表參加組合的有子物件的物件,並給出樹枝構件物件的行為。

3.  安全和透明式的合成模式

合成模式的實現根據實現介面的區別分為兩種形式,分別為安全式和透明式。他們的類圖分別如下:

 

 

透明方式:就是在Component裡面宣告所有的用來管理子物件的方法,包括add()remove()以及getChild()方法,這樣做的好處是所有的構件類都有相同的介面。在客戶端看來,樹葉類物件與合成類物件的區別起碼在介面層次上消失了,客戶端可以同等的對待所有的物件。這就是透明形式的合成模式。

        這個選擇的缺點是不夠安全的,因為樹葉類物件和合成類物件在本質上是有區別的,樹葉類物件不可能有一層的物件,因此add()remove()以及getChild()方法沒有意義,但是在編譯時期不會出錯,而只會在執行時期才會出錯。

       

安全方式:是在Composite類裡面宣告所有的用來管理子類物件的方法。這樣的做法是安全的做法,因為樹葉型別的物件根本就沒有管理子物件的方法。因此,如果客戶端對樹葉物件使用這些方法時會在編譯時期出錯。編譯不通過,就不會出現執行時出錯。

       這個選擇的缺點是不夠透明,因為樹葉類和合成類將具有不同的介面。

      

4.  組合模式的用法

在這裡使用組合模式對公司人事管理的樹狀機構進行描述:

首先建立抽象構建,原始碼如下:

package com.zsw.composite;

 

public abstract class Corp {

 

    private String name;     //名稱

    private String position; //職位

    private int salary = 0;  //薪水

   

    public Corp(String name,String position,int salary){

       this.name = name;

       this.position = position;

       this.salary = salary;

    }

   

    public String getInfo(){

       String info = "";

       info = "姓名:" + this.name;

       info = info + "/t職位:" + this.position;

       info = info + "/t薪水: " + this.salary;

       return info;

    }

}

 

建立樹葉構件原始碼如下:

package com.zsw.composite;

 

public class Leaf extends Corp {

 

    public Leaf(String name, String position, int salary) {

       super(name, position, salary);

    }

}

 

建立樹枝構件原始碼如下:

package com.zsw.composite;

import java.util.ArrayList;

public class Branch extends Corp {

 

    ArrayList<Corp> subordinateList = new ArrayList<Corp>();

   

    public Branch(String name, String position, int salary) {

       super(name, position, salary);

    }

 

    public void addSubordinate(Corp corp){

       this.subordinateList.add(corp);

    }

   

    public ArrayList<Corp> getSubordinate(){

       return this.subordinateList;

    }

}

 

 

客戶端原始碼如下:

package com.zsw.composite;

import java.util.ArrayList;

public class Client {

 

    public static void main(String[] args) {

       Branch ceo = compositeCorpTree();

       System.out.println(ceo.getInfo());

       System.out.println(getTreeInfo(ceo));

    }

   

   

    //把整個樹組裝出來

    public static Branch compositeCorpTree(){

       //總經理CEO

       Branch root = new Branch("王大","總經理",100000);

      

       //三個部門經理

       Branch developDep = new Branch("劉大","研發部門經理",10000);

       Branch salesDep = new Branch("劉大","銷售部門經理",20000);

       Branch financeDep = new Branch("劉大","財務部門經理",30000);

      

       //二個小組長

       Branch firstDevGroup = new Branch("楊三","開發一組組長",5000);

       Branch secondDevGroup = new Branch("吳大","開發二組組長",6000);

       Branch zhengLaoLiu = new Branch("鄭成功","研發部副經理",20000);

      

       //建立所有小兵

       Leaf a = new Leaf("a","開發人員",3000);

       Leaf b = new Leaf("b","開發人員",3000);

       Leaf c = new Leaf("c","開發人員",3000);

       Leaf d = new Leaf("d","開發人員",3000);

       Leaf e = new Leaf("e","開發人員",3000);

       Leaf f = new Leaf("f","開發人員",3000);

       Leaf g = new Leaf("g","開發人員",3000);

      

       Leaf h = new Leaf("h","銷售人員",5000);

       Leaf i = new Leaf("i","銷售人員",4000);

       Leaf j = new Leaf("j","財務人員",5000);

       Leaf k = new Leaf("k","CEO祕書",8000);

      

       //開始組裝

       //CEO下有三個部門經理和一個祕書

       root.addSubordinate(k);

       root.addSubordinate(developDep);

       root.addSubordinate(salesDep);

       root.addSubordinate(financeDep);

      

      

       //研發部經理

       developDep.addSubordinate(zhengLaoLiu);

       developDep.addSubordinate(firstDevGroup);

       developDep.addSubordinate(secondDevGroup);

      

       //2個小組

       firstDevGroup.addSubordinate(a);

       firstDevGroup.addSubordinate(b);

       firstDevGroup.addSubordinate(c);

       firstDevGroup.addSubordinate(d);

       secondDevGroup.addSubordinate(e);

       secondDevGroup.addSubordinate(f);

       secondDevGroup.addSubordinate(g);

      

       //銷售部門下人員情況

       salesDep.addSubordinate(h);

       salesDep.addSubordinate(i);

      

       //財務

       financeDep.addSubordinate(j);

      

       return root;

    }

   

    //遍歷整棵樹,只要給我根節點,我就能遍歷出所有的節點

    public static String getTreeInfo(Branch root){

       ArrayList<Corp> subordinateList = root.getSubordinate();

       String info = "";

      

       for(Corp s : subordinateList){

           if(s instanceof Leaf){

              info = info + s.getInfo() + "/n";

           }else{

              info = info + s.getInfo() + "/n" + getTreeInfo((Branch)s);

           }

       }

       return info;

    }

}

 

 

顯示結果如下:

姓名:王大  職位:總經理 薪水: 100000

姓名:k 職位:CEO祕書   薪水: 8000

姓名:劉大  職位:研發部門經理   薪水: 10000

姓名:鄭成功 職位:研發部副經理   薪水: 20000

姓名:楊三  職位:開發一組組長   薪水: 5000

姓名:a 職位:開發人員   薪水: 3000

姓名:b 職位:開發人員   薪水: 3000

姓名:c 職位:開發人員   薪水: 3000

姓名:d 職位:開發人員   薪水: 3000

姓名:吳大  職位:開發二組組長   薪水: 6000

姓名:e 職位:開發人員   薪水: 3000

姓名:f 職位:開發人員   薪水: 3000

姓名:g 職位:開發人員   薪水: 3000

姓名:劉大  職位:銷售部門經理   薪水: 20000

姓名:h 職位:銷售人員   薪水: 5000

姓名:i 職位:銷售人員   薪水: 4000

姓名:劉大  職位:財務部門經理   薪水: 30000

姓名:j 職位:財務人員   薪水: 5000

 

      

5.  組合模式的應用場景

a)         一個檔案系統就是一個典型的合成模式系統。可以把目錄和檔案同等對待和處理,這也就是合成模式的應用。

b)        道士的故事,哄小孩睡覺講故事,從前有座山、山裡有個廟、廟裡有個老道,老道講了一個故事,故事說從前有座山、山裡有個廟…..這樣迴圈下去直到小孩睡著。在這個故事中有兩種角色:一種沒有內部角色,例如山、廟、道士;另一種是有內部角色的的角色,在這個故事中的故事。故事裡又有、山、廟、道士….此故事的UML類圖如下:

 

 

c)        公司的人事管理就是一個典型的樹狀結構,公司的結構如下圖所示,從最高的老大,往下一層的管理,最後到我們這層小兵,很典型的樹狀結構。

 

 

d)        ASP.NETTreeView控制元件既是典型的組合模式應用,所有WEB控制元件的基類都是System.Web.UI.Control,Control基類中就有AddRemove方法,這就是典型的組合模式的應用。

e)         比如現在頁面結構一般都是上下結構,上放系統的Logo,下邊分為兩部分:左邊是導航選單,右邊是展示區,左邊的導航選單一般都是樹形結構,比較清晰.

f)         AWT庫中的例子,AWTSwing中的圖形介面構件是建立在AWT庫中的Container類和Component類上的。其中ButtonCheckBox是樹葉形的構件,而Container則是樹枝行的構件。如AWT合成模式的UML類圖:

 

Container類中,有操作聚集的對應的方法,而在Component類中沒有這樣的方法。這就是說AWT總使用的合成模式是安全形式的合成模式。

 

g)        需要描述物件的部分和整體的等級結構。

h)        需要客戶端忽略掉個體構件和組合構件的區別。客戶端必須平等對待所有的構件,包括個體元件和組合構件。

i)          合成模式可以很容易的增加新種類的構件。

j)          使用合成模式可以是客戶端變得很容易設計,因為客戶端不需要知道構件時樹葉構件還是樹枝構件。

 

6.  組合模式的優缺點

優點:

缺點:

ü         使用合成模式後,控制樹枝構件的型別就不太容易。

ü         用繼承的方法增加新的行為很困難。

7.  參考文件

a)         Java與模式》

b)        《大話設計模式》

相關文章