Java設計模式——組合模式(Composite Pattern)
場景一
描述:大家在上學的時候應該都學過“資料結構”這門課程吧,還記得其中有一節叫“二叉樹”吧,我們上學那會兒這一章節是必考內容,左子樹,右子樹,什麼先序遍歷後序遍歷什麼,重點就是二叉樹的的遍歷,我還記得當時老師就說,考試的時候一定有二叉樹的構建和遍歷,現在想起來還是覺的老師是正確的,樹狀結果在實際專案應用的非常廣泛。
咱就先說個最常見的例子,公司的人事管理就是一個典型的樹狀結構,你想想你公司的結構是不是這樣:
從最高的老大,往下一層一層的管理,最後到我們這層小兵,很典型的樹狀結構(說明一下,這不是二叉樹,有關二叉樹的定義可以翻翻以前的教科書),我們今天的任務就是要把這個樹狀結構實現出來,並且還要把它遍歷一遍,你要確認你建立的樹是否有問題呀。
從這個樹狀結構上分析,有兩種節點:有分支的節點(如研發部經理)和無分支的節點(如員工A、員工D 等),我們增加一點學術術語上去,總經理叫做根節點(是不是想到XML 中的那個根節點root,那就對了),類似研發部經理有分支的節點叫做樹枝節點,類似員工A 的無分支的節點叫做樹葉節點,都很形象,三個型別的的節點,那是不是定義三個類就可以?好,我們按照這個思路走下去,先看我們自己設計的類圖:
這個類圖是初學者最容易想到的類圖(如果你已經看明白這個類圖的缺陷了,就可以不看下邊的實現了,我是循序漸進的講課,呵呵),我那來看這個實現:
先看最高級別的根節點的實現:
這個根節點就是我們的總經理CEO,然後看實現類: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(); }
很簡單,通過建構函式傳入引數,然後獲得資訊,還可以增加子樹枝節點(部門經理)和葉子節點(祕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 屬性,什麼後序遍歷(從下往上找)、中序遍歷(從中間某個環節往上或往下遍歷)都解決了,這個就不多說了。再提一個問題,樹葉節點和樹枝節點是有順序的,你不能亂排的,怎麼辦?比如我們上面的例子,研發一組下邊有三個成員,這三個成員是要進行排序的呀,你怎麼處理?問我呀,問你呢,好好想想,以後
用到著的!