1. 程式人生 > >Java設計模式——組合模式(Composite Pattern)

Java設計模式——組合模式(Composite Pattern)

場景一

描述:大家在上學的時候應該都學過“資料結構”這門課程吧,還記得其中有一節叫“二叉樹”吧,我們上學那會兒這一章節是必考內容,左子樹,右子樹,什麼先序遍歷後序遍歷什麼,重點就是二叉樹的的遍歷,我還記得當時老師就說,考試的時候一定有二叉樹的構建和遍歷,現在想起來還是覺的老師是正確的,樹狀結果在實際專案應用的非常廣泛。
        咱就先說個最常見的例子,公司的人事管理就是一個典型的樹狀結構,你想想你公司的結構是不是這樣:

        從最高的老大,往下一層一層的管理,最後到我們這層小兵,很典型的樹狀結構(說明一下,這不是二叉樹,有關二叉樹的定義可以翻翻以前的教科書),我們今天的任務就是要把這個樹狀結構實現出來,並且還要把它遍歷一遍,你要確認你建立的樹是否有問題呀。
         從這個樹狀結構上分析,有兩種節點:有分支的節點(如研發部經理)和無分支的節點(如員工A、員工D 等),我們增加一點學術術語上去,總經理叫做根節點(是不是想到XML 中的那個根節點root,那就對了),類似研發部經理有分支的節點叫做樹枝節點,類似員工A 的無分支的節點叫做樹葉節點,都很形象,三個型別的的節點,那是不是定義三個類就可以?好,我們按照這個思路走下去,先看我們自己設計的類圖:

        這個類圖是初學者最容易想到的類圖(如果你已經看明白這個類圖的缺陷了,就可以不看下邊的實現了,我是循序漸進的講課,呵呵),我那來看這個實現:
        先看最高級別的根節點的實現:

package com.gumx.common;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 定義一個根節點,就為總經理服務
*/
public interface IRoot {
    //得到總經理的資訊
    public String getInfo();
    //總經理下邊要有小兵,那要能增加小兵,比如研發部總經理,這是個樹枝節點
    public void add(IBranch branch);
    //那要能增加樹葉節點
    public void add(ILeaf leaf);
    //既然能增加,那要還要能夠遍歷,不可能總經理不知道他手下有哪些人
    public ArrayList getSubordinateInfo();
}
這個根節點就是我們的總經理CEO,然後看實現類:
package com.gumx.common;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 根節點的實現類
*/
@SuppressWarnings("all")
public class Root implements IRoot {
    //儲存根節點下的樹枝節點和樹葉節點,Subordinate的意思是下級
    private ArrayList subordinateList = new ArrayList();
    //根節點的名稱
    private String name = "";
    //根節點的職位
    private String position = "";
    //根節點的薪水
    private int salary = 0;
    //通過建構函式傳遞進來總經理的資訊
    public Root(String name,String position,int salary){
        this.name = name;
        this.position = position;
        this.salary = salary;
    }
    //增加樹枝節點
    public void add(IBranch branch) {
        this.subordinateList.add(branch);
    }
    //增加葉子節點,比如祕書,直接隸屬於總經理
    public void add(ILeaf leaf) {
        this.subordinateList.add(leaf);
    }
    //得到自己的資訊
    public String getInfo() {
        String info = "";
        info = "名稱:"+ this.name;;
        info = info + "\t職位:" + this.position;
        info = info + "\t薪水: " + this.salary;
        return info;
    }
    //得到下級的資訊
    public ArrayList getSubordinateInfo() {
        return this.subordinateList;
    }
}
很簡單,通過建構函式傳入引數,然後獲得資訊,還可以增加子樹枝節點(部門經理)和葉子節點(祕
書)。我們再來看IBranch.java:
package com.gumx.common;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 樹枝節點,也就是各個部門經理和組長的角色
*/
public interface IBranch {
    //獲得資訊
    public String getInfo();
    //增加資料節點,例如研發部下的研發一組
    public void add(IBranch branch);
    //增加葉子節點
    public void add(ILeaf leaf);
    //獲得下級資訊
    public ArrayList getSubordinateInfo();
}
下面是樹枝節點的實現類:
package com.gumx.common;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 所有的樹枝節點
*/
@SuppressWarnings("all")
public class Branch implements IBranch {
    //儲存子節點的資訊
    private ArrayList subordinateList = new ArrayList();
    //樹枝節點的名稱
    private String name="";
    //樹枝節點的職位
    private String position = "";
    //樹枝節點的薪水
    private int salary = 0;
    //通過建構函式傳遞樹枝節點的引數
    public Branch(String name,String position,int salary){
        this.name = name;
        this.position = position;
        this.salary = salary;
    }
    //增加一個子樹枝節點
    public void add(IBranch branch) {
        this.subordinateList.add(branch);
    }
    //增加一個葉子節點
    public void add(ILeaf leaf) {
        this.subordinateList.add(leaf);
    }
    //獲得自己樹枝節點的資訊
    public String getInfo() {
        String info = "";
        info = "名稱:" + this.name;
        info = info + "\t職位:"+ this.position;
        info = info + "\t薪水:"+this.salary;
        return info;
    }
    //獲得下級的資訊
    public ArrayList getSubordinateInfo() {
        return this.subordinateList;
    }
}
最後看葉子節點,也就是員工的介面:
package com.gumx.common;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 葉子節點,也就是最小的小兵了,只能自己幹活,不能指派別人了
*/
public interface ILeaf {
    //獲得自己的資訊呀
    public String getInfo();
}
下面是葉子節點的實現類:
package com.gumx.common;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 最小的葉子節點
*/
@SuppressWarnings("all")
public class Leaf implements ILeaf {
    //葉子叫什麼名字
    private String name = "";
    //葉子的職位
    private String position = "";
    //葉子的薪水
    private int salary=0;
    //通過建構函式傳遞資訊
    public Leaf(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;
    }
}
好了,所有的根節點,樹枝節點和葉子節點都已經實現了,從總經理、部門經理到最終的員工都已經實現了,然後的工作就是組裝成一個樹狀結構和遍歷這個樹狀結構,看Client.java 程式:
package com.gumx.common;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* Client的作用是組裝這棵樹,並遍歷一遍
*/
@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) {
        //首先產生了一個根節點
        IRoot ceo = new Root("王大麻子","總經理",100000);
        //產生三個部門經理,也就是樹枝節點
        IBranch developDep = new Branch("劉大瘸子","研發部門經理",10000);
        IBranch salesDep = new Branch("馬二柺子","銷售部門經理",20000);
        IBranch financeDep = new Branch("趙三駝子","財務部經理",30000);
        //再把三個小組長產生出來
        IBranch firstDevGroup = new Branch("楊三乜斜","開發一組組長",5000);
        IBranch secondDevGroup = new Branch("吳大棒槌","開發二組組長",6000);
        //剩下的及時我們這些小兵了,就是路人甲,路人乙
        ILeaf a = new Leaf("a","開發人員",2000);
        ILeaf b = new Leaf("b","開發人員",2000);
        ILeaf c = new Leaf("c","開發人員",2000);
        ILeaf d = new Leaf("d","開發人員",2000);
        ILeaf e = new Leaf("e","開發人員",2000);
        ILeaf f = new Leaf("f","開發人員",2000);
        ILeaf g = new Leaf("g","開發人員",2000);
        ILeaf h = new Leaf("h","銷售人員",5000);
        ILeaf i = new Leaf("i","銷售人員",4000);
        ILeaf j = new Leaf("j","財務人員",5000);
        ILeaf k = new Leaf("k","CEO祕書",8000);
        ILeaf zhengLaoLiu = new Leaf("鄭老六","研發部副總",20000);
        //該產生的人都產生出來了,然後我們怎麼組裝這棵樹
        //首先是定義總經理下有三個部門經理
        ceo.add(developDep);
        ceo.add(salesDep);
        ceo.add(financeDep);
        //總經理下還有一個祕書
        ceo.add(k);
        //定義研發部門下的結構
        developDep.add(firstDevGroup);
        developDep.add(secondDevGroup);
        //研發部經理下還有一個副總
        developDep.add(zhengLaoLiu);
        //看看開發兩個開發小組下有什麼
        firstDevGroup.add(a);
        firstDevGroup.add(b);
        firstDevGroup.add(c);
        secondDevGroup.add(d);
        secondDevGroup.add(e);
        secondDevGroup.add(f);
        //再看銷售部下的人員情況
        salesDep.add(h);
        salesDep.add(i);
        //最後一個財務
        financeDep.add(j);
        //樹狀結構寫完畢,然後我們打印出來
        System.out.println(ceo.getInfo());
        //打印出來整個樹形
        getAllSubordinateInfo(ceo.getSubordinateInfo());
    }
    //遍歷所有的樹枝節點,打印出資訊
    private static void getAllSubordinateInfo(ArrayList subordinateList){
        int length = subordinateList.size();
        for(int m=0;m<length;m++){ //定義一個ArrayList長度,不要在for迴圈中每次計算
            Object s = subordinateList.get(m);
            if(s instanceof Leaf){ //是個葉子節點,也就是員工
                ILeaf employee = (ILeaf)s;
                System.out.println(((Leaf) s).getInfo());
            }else{
                IBranch branch = (IBranch)s;
                System.out.println(branch.getInfo());
                //再遞迴呼叫
                getAllSubordinateInfo(branch.getSubordinateInfo());
            }
        }
    }
}
這個程式比較長,如果是在我們的專案中有這樣的程式,肯定是被拉出來做典型的,你寫一大坨的程式給誰呀,以後還要維護的,程式是要短小精悍!幸運的是,我們是這為案例來講解,而且就是指出這樣組裝這棵樹是有問題,等會我們深入講解,先看執行結果:

名稱:王大麻子職位:總經理薪水: 100000
名稱:劉大瘸子職位:研發部門經理薪水:10000
名稱:楊三乜斜職位:開發一組組長薪水:5000
名稱:a 職位:開發人員薪水:2000
名稱:b 職位:開發人員薪水:2000
名稱:c 職位:開發人員薪水:2000
名稱:吳大棒槌職位:開發二組組長薪水:6000
名稱:d 職位:開發人員薪水:2000
名稱:e 職位:開發人員薪水:2000
名稱:f 職位:開發人員薪水:2000
名稱:鄭老六職位:研發部副總薪水:20000名稱:馬二柺子職位:銷售部門經理薪水:20000
名稱:h 職位:銷售人員薪水:5000
名稱:i 職位:銷售人員薪水:4000
名稱:趙三駝子職位:財務部經理薪水:30000
名稱:j 職位:財務人員薪水:5000
名稱:k 職位:CEO祕書薪水:8000

         和我們期望要的結果一樣,一棵完整的樹就生成了,而且我們還能夠遍歷。看類圖或程式的時候,你有沒有發覺有問題?getInfo 每個介面都有為什麼不能抽象出來?Root 類和Branch 類有什麼差別?為什麼要定義成兩個介面兩個類?如果我要加一個任職期限,你是不是每個類都需要修改?如果我要後序遍歷(從員工找到他的上級領導)能做嗎?——徹底暈菜了!

        問題很多,我們一個一個解決,先說抽象的問題,確實可以吧IBranch 和IRoot 合併成一個介面,這個我們先肯定下來,這是個比較大的改動,我們先畫個類圖:

        這個類圖還是有點問題的,介面的作用是什麼?定義共性,那ILeaf 和IBranch 是不是也有共性呢?有getInfo(),我們是不是要把這個共性也已經封裝起來呢?好,我們再修改一下類圖:

        類圖上有兩個介面,ICorp 是公司所有人員的資訊的介面類,不管你是經理還是員工,你都有名字,職位,薪水,這個定義成一個介面沒有錯,IBranch 有沒有必要呢?我們先實現出來然後再說。
        先看ICorp.java 原始碼:

package com.gumx.advance;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 公司類,定義每個員工都有資訊
*/
public interface ICorp {
    //每個員工都有資訊,你想隱藏,門兒都沒有!
    public String getInfo();
}
介面很簡單,只有一個方法,就是獲得員工的資訊,我們再來看實現類:
package com.gumx.advance;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* Leaf是樹葉節點,在這裡就是我們這些小兵
*/
@SuppressWarnings("all")
public class Leaf implements ICorp {
    //小兵也有名稱
    private String name = "";
    //小兵也有職位
    private String position = "";
    //小兵也有薪水,否則誰給你幹
    private int salary = 0;
    //通過一個建構函式傳遞小兵的資訊
    public Leaf(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.gumx.advance;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 這些下邊有小兵或者是經理等風雲人物
*/
public interface IBranch {
    //能夠增加小兵(樹葉節點)或者是經理(樹枝節點)
    public void addSubordinate(ICorp corp);
    //我還要能夠獲得下屬的資訊
    public ArrayList<ICorp> getSubordinate();
    /*本來還應該有一個方法delSubordinate(ICorp corp),刪除下屬
    * 這個方法我們沒有用到就不寫進來了
    */
}
介面也是很簡單的,下面是實現類:
package com.gumx.advance;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 這些樹枝節點也就是這些領導們既要有自己的資訊,還要知道自己的下屬情況
*/
@SuppressWarnings("all")
public class Branch implements IBranch, ICorp {
    //領導也是人,也有名字
    private String name = "";
    //領導和領導不同,也是職位區別
    private String position = "";
    //領導也是拿薪水的
    private int salary = 0;
    //領導下邊有那些下級領導和小兵
    ArrayList<ICorp> subordinateList = new ArrayList<ICorp>();
    //通過建構函式傳遞領導的資訊
    public Branch(String name,String position,int salary){
        this.name = name;
        this.position = position;
        this.salary = salary;
    }
    //增加一個下屬,可能是小頭目,也可能是個小兵
    public void addSubordinate(ICorp corp) {
        this.subordinateList.add(corp);
    }
    //我有哪些下屬
    public ArrayList<ICorp> getSubordinate() {
        return this.subordinateList;
    }
    //領導也是人,他也有資訊
    public String getInfo() {
        String info = "";
        info = "姓名:" + this.name;
        info = info + "\t職位:"+ this.position;
        info = info + "\t薪水:" + this.salary;
        return info;
    }
}
實現類也很簡單,不多說,程式寫的好不好,就看別人怎麼呼叫了,我們看Client.java 程式:
package com.gumx.advance;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 組裝這個樹形結構,並展示出來
*/
@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) {
        //首先是組裝一個組織結構出來
        Branch ceo = compositeCorpTree();
        //首先把CEO的資訊打印出來:
        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);
        //把所有的小兵都產生出來
        Leaf a = new Leaf("a","開發人員",2000);
        Leaf b = new Leaf("b","開發人員",2000);
        Leaf c = new Leaf("c","開發人員",2000);
        Leaf d = new Leaf("d","開發人員",2000);
        Leaf e = new Leaf("e","開發人員",2000);
        Leaf f = new Leaf("f","開發人員",2000);
        Leaf g = new Leaf("g","開發人員",2000);
        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);
        Leaf zhengLaoLiu = new Leaf("鄭老六","研發部副經理",20000);
        //開始組裝
        //CEO下有三個部門經理和一個祕書
        root.addSubordinate(k);
        root.addSubordinate(developDep);
        root.addSubordinate(salesDep);
        root.addSubordinate(financeDep);
        //研發部經理
        developDep.addSubordinate(zhengLaoLiu);
        developDep.addSubordinate(firstDevGroup);
        developDep.addSubordinate(secondDevGroup);
        //看看開發兩個開發小組下有什麼
        firstDevGroup.addSubordinate(a);
        firstDevGroup.addSubordinate(b);
        firstDevGroup.addSubordinate(c);
        secondDevGroup.addSubordinate(d);
        secondDevGroup.addSubordinate(e);
        secondDevGroup.addSubordinate(f);
        //再看銷售部下的人員情況
        salesDep.addSubordinate(h);
        salesDep.addSubordinate(i);
        //最後一個財務
        financeDep.addSubordinate(j);
        return root;
    }
    //遍歷整棵樹,只要給我根節點,我就能遍歷出所有的節點
    public static String getTreeInfo(Branch root){
        ArrayList<ICorp> subordinateList = root.getSubordinate();
        String info = "";
        for(ICorp 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 職位:開發人員薪水:2000姓名:b 職位:開發人員薪水:2000
姓名:c 職位:開發人員薪水:2000
姓名:吳大棒槌職位:開發二組組長薪水:6000
姓名:d 職位:開發人員薪水:2000
姓名:e 職位:開發人員薪水:2000
姓名:f 職位:開發人員薪水:2000
姓名:馬二柺子職位:銷售部門經理薪水:20000
姓名:h 職位:銷售人員薪水:5000
姓名:i 職位:銷售人員薪水:4000
姓名:趙三駝子職位:財務部經理薪水:30000
姓名:j 職位:財務人員薪水:5000

一個非常清理的樹狀人員資源管理圖出現了,那我們的程式是否還可以優化?可以!你看Leaf 和Branch中都有getInfo 資訊,是否可以抽象,好,我們抽象一下:



你一看這個圖,樂了,能不樂嘛,減少很多工作量了,介面沒有了,改成抽象類了,IBranch 介面也沒有了,直接把方法放到了實現類中了,那我們先來看抽象類:

package com.gumx.perfect;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 定義一個公司的人員的抽象類
*/
@SuppressWarnings("all")
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.gumx.perfect;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 普通員工很簡單,就寫一個建構函式就可以了
*/
public class Leaf extends Corp {
    //就寫一個建構函式,這個是必須的
    public Leaf(String _name,String _position,int _salary){
        super(_name,_position,_salary);
    }
}
這個改動比較多,就幾行程式碼就完成了,確實就應該這樣,下面是小頭目的實現類:
package com.gumx.perfect;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 節點類,也簡單了很多
*/
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;
    }
}
也縮減了很多,再看Client.java 程式,這個就沒有多大變化了:
package com.gumx.perfect;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 組裝這個樹形結構,並展示出來
*/
@SuppressWarnings("all")
public class Client {
    public static void main(String[] args) {
        //首先是組裝一個組織結構出來
        Branch ceo = compositeCorpTree();
        //首先把CEO的資訊打印出來:
        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);
        //把所有的小兵都產生出來
        Leaf a = new Leaf("a","開發人員",2000);
        Leaf b = new Leaf("b","開發人員",2000);
        Leaf c = new Leaf("c","開發人員",2000);
        Leaf d = new Leaf("d","開發人員",2000);
        Leaf e = new Leaf("e","開發人員",2000);
        Leaf f = new Leaf("f","開發人員",2000);
        Leaf g = new Leaf("g","開發人員",2000);
        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);
        Leaf zhengLaoLiu = new Leaf("鄭老六","研發部副經理",20000);
        //開始組裝
        //CEO下有三個部門經理和一個祕書
        root.addSubordinate(k);
        root.addSubordinate(developDep);
        root.addSubordinate(salesDep);
        root.addSubordinate(financeDep);
        //研發部經理
        developDep.addSubordinate(zhengLaoLiu);
        developDep.addSubordinate(firstDevGroup);
        developDep.addSubordinate(secondDevGroup);
        //看看開發兩個開發小組下有什麼
        firstDevGroup.addSubordinate(a);
        firstDevGroup.addSubordinate(b);
        firstDevGroup.addSubordinate(c);
        secondDevGroup.addSubordinate(d);
        secondDevGroup.addSubordinate(e);
        secondDevGroup.addSubordinate(f);
        //再看銷售部下的人員情況
        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;
    }
}
就是把用到ICorp 介面的地方修改為Corp 抽象類就成了,就上面黃色的部分作了點修改,其他保持不
變,執行結果還是保持一樣:

姓名:王大麻子職位:總經理薪水:100000
姓名:k 職位:CEO祕書薪水:8000
姓名:劉大瘸子職位:研發部門經理薪水:10000
姓名:鄭老六職位:研發部副經理薪水:20000
姓名:楊三乜斜職位:開發一組組長薪水:5000
姓名:a 職位:開發人員薪水:2000
姓名:b 職位:開發人員薪水:2000
姓名:c 職位:開發人員薪水:2000
姓名:吳大棒槌職位:開發二組組長薪水:6000
姓名:d 職位:開發人員薪水:2000
姓名:e 職位:開發人員薪水:2000
姓名:f 職位:開發人員薪水:2000
姓名:馬二柺子職位:銷售部門經理薪水:20000
姓名:h 職位:銷售人員薪水:5000
姓名:i 職位:銷售人員薪水:4000
姓名:趙三駝子職位:財務部經理薪水:30000
姓名:j 職位:財務人員薪水:5000

        確實是類、介面減少了很多,而且程式也簡單很多,但是大家可能還是很迷茫,這個Client 程式並沒有改變多少呀,非常正確,樹的組裝你是跑不了的,你要知道在專案中使用資料庫來儲存這些資訊的,你從資料庫中提出出來哪些人要分配到樹枝,哪些人要分配到樹葉,樹枝與樹枝、樹葉的關係,這些都需要人去定義,通常這裡使用一個介面去配置,在資料庫中是一個標誌資訊,例如定義這樣一張表:


        從這張表中已經定義個一個樹形結構,我們要做的就是從資料庫中讀取出來,然後展現到前臺上,這個讀取就用個for 迴圈加上遞迴是不是就可以把一棵樹建立起來?我們程式中其實還包涵了資料的讀取和加工,用了資料庫後,資料和邏輯已經在表中定義好了,我們直接讀取放到樹上就可以了,這個還是比較容易做了的,大家不妨自己考慮一下。
        上面我們講到的就是組合模式(也叫合成模式),有時又叫做部分-整體模式(Part-Whole),主要是用來描述整體與部分的關係,用的最多的地方就是樹形結構。組合模式通用類圖如下:


我們先來說說組合模式的幾個角色:
抽象構件角色(Component):定義參加組合的物件的共有方法和屬性,可以定義一些預設的行為或屬性;
比如我們例子中的getInfo 就封裝到了抽象類中。
葉子構件(Leaf):葉子物件,其下再也沒有其他的分支。
樹枝構件(Composite):樹枝物件,它的作用是組合樹枝節點和葉子節點;
組合模式有兩種模式,透明模式和安全模式,這兩個模式有什麼區別呢?先看類圖:

                                                                      透明模式類圖

                                                                               安全模式類圖

        從類圖上大家應該能看清楚了,這兩種模式各有優缺點,透明模式是把用來組合使用的方法放到抽象類中,比如add(),remove()以及getChildren 等方法(順便說一下,getChildren 一般返回的結果為Iterable的實現類,很多,大家可以看JDK 的幫助),不管葉子物件還是樹枝物件都有相同的結構,通過判斷是getChildren 的返回值確認是葉子節點還是樹枝節點,如果處理不當,這個會在執行期出現問題的,不是很建議的方式;安全模式就不同了,它是把樹枝節點和樹葉節點徹底分開,樹枝節點單獨擁有用來組合的方法,這種方法比較安全,我們的例子使用了安全模式。組合模式的優點有哪些呢?第一個優點只要是樹形結構,就要考慮使用組合模式,這個一定記住,只要是要體現區域性和整體的關係的時候,而且這種關係還可能比較深,考慮一下組合模式吧。組合模式有一個非常明顯的缺點,看到我們在Client.java 中的的定義了樹葉和樹枝使用時的定義了嗎?如下:

Branch developDep = new Branch("劉大瘸子","研發部門經理",10000);
…
Leaf g = new Leaf("g","開發人員",2000);
        發現什麼問題了嗎?直接使用了實現類!這個在面向介面程式設計上是很不恰當的,這個在使用的時候要考慮清楚。
        組合模式在專案中到處都有,比如現在的頁面結構一般都是上下結構,上面放系統的Logo,下邊分為兩部分:左邊是導航選單,右邊是展示區,左邊的導航選單一般都是樹形的結構,比較清晰,這個JavaScript有很多例子,大家可以到網上搜索一把;還有,我們的自己也是一個樹狀結構,根據我,能夠找到我的父母,根據父親又能找到爺爺奶奶,根據母親能夠找到外公外婆等等,很典型的樹形結構,而且還很規範(這個要是不規範那肯定是亂套了)。

         我們在上面也還提到了一個問題,就是樹的遍歷問題,從上到下遍歷沒有問題,但是我要是從下往上遍歷呢?比如在人力資源這顆樹上,我從中抽取一個使用者,要找到它的上級有哪些,下級有哪些,怎麼處理?想想,~~~,再想想!想出來了吧,我們對下答案,先看類圖:


看類圖中的紅色方框,只要增加兩個方法就可以了,一個是設定父節點是誰,一個是查詢父節點是誰,我們來看一下程式的改變:

package com.gumx.extend;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 定義一個公司的人員的抽象類
*/
@SuppressWarnings("all")
public abstract class Corp {
    //公司每個人都有名稱
    private String name = "";
    //公司每個人都職位
    private String position = "";
    //公司每個人都有薪水
    private int salary =0;
    //父節點是誰
<span style="background-color: rgb(255, 255, 51);">    private Corp parent = null;</span>
    /*通過介面的方式傳遞,我們改變一下習慣,傳遞進來的引數名以下劃線開始
    * 這個在一些開源專案中非常常見,一般建構函式都是定義的
    */
    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;
    }
<span style="background-color: rgb(255, 255, 51);">    //設定父節點
    protected void setParent(Corp _parent){
        this.parent = _parent;
    }
    //得到父節點
    public Corp getParent(){
        return this.parent;
    }</span>
}
就增加了黃色部分,然後我們再來看看Branch.java 的改變:
package com.gumx.extend;
import java.util.ArrayList;
/**
* @author gumx
* I'm glad to share my knowledge with you all.
* 節點類,也簡單了很多
*/
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) {
       <span style="background-color: rgb(255, 255, 51);"> corp.setParent(this); //設定父節點</span>
        this.subordinateList.add(corp);
    }
    //我有哪些下屬
    public ArrayList<Corp> getSubordinate() {
        return this.subordinateList;
    }
}
        增加了黃色部分,看懂程式了嗎?就是在每個節點甭管是樹枝節點還是樹葉節點,都增加了一個屬性:父節點物件,這樣在樹枝節點增加子節點或葉子的時候設定父節點,然後你看整棵樹就除了根節點外每個節點都一個父節點,剩下的事情還不好處理嗎?每個節點上都有父節點了,你要往上找,那就找唄!Client程式我就不寫了,今天已經拷貝的程式碼實在有點多,大家自己考慮一下,寫個find 方法,然後一個一個往上找,最簡單的方法了!
        有了這個parent 屬性,什麼後序遍歷(從下往上找)、中序遍歷(從中間某個環節往上或往下遍歷)都解決了,這個就不多說了。再提一個問題,樹葉節點和樹枝節點是有順序的,你不能亂排的,怎麼辦?比如我們上面的例子,研發一組下邊有三個成員,這三個成員是要進行排序的呀,你怎麼處理?問我呀,問你呢,好好想想,以後
用到著的!