公司的人事管理是一個(gè)典型的樹(shù)狀結(jié)構(gòu):
我們今天的任務(wù)就是要把這個(gè)樹(shù)狀結(jié)構(gòu)實(shí)現(xiàn)出來(lái)匾寝,并且還要把它遍歷一遍.
從這個(gè)樹(shù)狀結(jié)構(gòu)上分析,有兩種節(jié)點(diǎn):有分支的節(jié)點(diǎn)(如研發(fā)部經(jīng)理)和無(wú)分支的節(jié)點(diǎn)(如員工A、員工D等)僻澎,總經(jīng)理叫做根節(jié)點(diǎn)废封,類似研發(fā)部經(jīng)理有分支的節(jié)點(diǎn)叫做樹(shù)枝節(jié)點(diǎn)州泊,類似員工A的無(wú)分支的節(jié)點(diǎn)叫做樹(shù)葉節(jié)點(diǎn),三個(gè)類型的的節(jié)點(diǎn)漂洋,那是不是定義三個(gè)類就可以遥皂?好力喷,我們按照這個(gè)思路走下去,先看我們自己設(shè)計(jì)的類圖:
以下是上述類圖的實(shí)現(xiàn):
/**
* 定義一個(gè)根節(jié)點(diǎn)演训,就為總經(jīng)理服務(wù)
*/
public interface IRoot {
// 得到總經(jīng)理的信息
String getInfo();
// 總經(jīng)理下邊要有小兵弟孟,那要能增加小兵,比如研發(fā)部經(jīng)理样悟,這是個(gè)樹(shù)枝節(jié)點(diǎn)
void add(IBranch branch);
// 增加樹(shù)葉節(jié)點(diǎn)
void add(ILeaf leaf);
// 遍歷下屬
ArrayList<Object> getSubordinateInfo();
}
/**
* 樹(shù)枝節(jié)點(diǎn)拂募,也就是各個(gè)部門(mén)經(jīng)理和組長(zhǎng)的角色
*/
public interface IBranch {
// 獲取信息
String getInfo();
// 增加數(shù)據(jù)節(jié)點(diǎn),例如研發(fā)部下的研發(fā)一組
void add(IBranch branch);
// 增加樹(shù)葉節(jié)點(diǎn)
void add(ILeaf leaf);
// 獲取下級(jí)信息
ArrayList<Object> getSubordinateInfo();
}
/**
* 葉子節(jié)點(diǎn)窟她,也就是最小的小兵了陈症,只能自己干活,不能指派別人了
*/
public interface ILeaf {
// 獲得自己的信息
String getInfo();
}
/**
* 根節(jié)點(diǎn)的實(shí)現(xiàn)類
*/
public class Root implements IRoot {
// 保存根節(jié)點(diǎn)下的樹(shù)枝節(jié)點(diǎn)和樹(shù)葉節(jié)點(diǎn)震糖,subordinate是下級(jí)的意思
private ArrayList<Object> subordinateList = new ArrayList<>();
// 根節(jié)點(diǎn)的名稱
private String name = "";
// 根節(jié)點(diǎn)的職位
private String position = "";
// 根節(jié)點(diǎn)的薪水
private int salary = 0;
// 通過(guò)構(gòu)造函數(shù)傳遞進(jìn)來(lái)總經(jīng)理的信息
public Root(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
// 得到自己的信息
@Override
public String getInfo() {
return String.format("名稱: %s\t職位: %s\t薪水: %s", this.name, this.position, this.salary);
}
// 增加樹(shù)枝節(jié)點(diǎn)
@Override
public void add(IBranch branch) {
this.subordinateList.add(branch);
}
// 增加葉子節(jié)點(diǎn)录肯,比如秘書(shū),直接隸屬于總經(jīng)理
@Override
public void add(ILeaf leaf) {
this.subordinateList.add(leaf);
}
// 獲得下級(jí)的信息
@Override
public ArrayList<Object> getSubordinateInfo() {
return this.subordinateList;
}
}
/**
* 樹(shù)枝節(jié)點(diǎn)吊说,就是各個(gè)部門(mén)經(jīng)理和組長(zhǎng)的角色
*/
public class Branch implements IBranch {
// 存儲(chǔ)子節(jié)點(diǎn)的信息
private ArrayList<Object> subordinateList = new ArrayList<>();
// 樹(shù)枝節(jié)點(diǎn)的名稱
private String name = "";
// 樹(shù)枝節(jié)點(diǎn)的職位
private String position = "";
// 樹(shù)枝節(jié)點(diǎn)的薪水
private int salary = 0;
// 通過(guò)構(gòu)造函數(shù)傳遞樹(shù)枝節(jié)點(diǎn)的參數(shù)
public Branch(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
// 獲得自己樹(shù)枝節(jié)點(diǎn)的信息
@Override
public String getInfo() {
return String.format("名稱: %s\t職位: %s\t薪水: %s", this.name, this.position, this.salary);
}
// 增加一個(gè)子樹(shù)枝節(jié)點(diǎn)
@Override
public void add(IBranch branch) {
this.subordinateList.add(branch);
}
// 增加一個(gè)葉子節(jié)點(diǎn)
@Override
public void add(ILeaf leaf) {
this.subordinateList.add(leaf);
}
// 獲得下級(jí)的信息
@Override
public ArrayList<Object> getSubordinateInfo() {
return this.subordinateList;
}
}
/**
* 最小的葉子節(jié)點(diǎn)
*/
public class Leaf implements ILeaf {
// 葉子叫什么名字
private String name = "";
// 葉子的職位
private String position = "";
// 葉子的薪水
private int salary = 0;
// 通過(guò)構(gòu)造函數(shù)傳遞信息
public Leaf(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
// 最小的小兵只能獲得自己的信息了
@Override
public String getInfo() {
return String.format("名稱: %s\t職位: %s\t薪水: %s", this.name, this.position, this.salary);
}
}
好了论咏,所有的根節(jié)點(diǎn),樹(shù)枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)都已經(jīng)實(shí)現(xiàn)了疏叨,從總經(jīng)理潘靖、部門(mén)經(jīng)理到最終的員工都已經(jīng)實(shí)現(xiàn)了,然后的工作就是組裝成一個(gè)樹(shù)狀結(jié)構(gòu)和遍歷這個(gè)樹(shù)狀結(jié)構(gòu)蚤蔓,看Client類:
/**
* Client的作用是組裝這棵樹(shù)卦溢,并遍歷一遍
*/
public class Client {
public static void main(String[] args) {
// 首先產(chǎn)生了一個(gè)根節(jié)點(diǎn)
IRoot ceo = new Root("王大麻子", "CEO", 100000);
// 產(chǎn)生三個(gè)部門(mén)經(jīng)理,也就是樹(shù)枝節(jié)點(diǎn)
IBranch developDep = new Branch("劉大瘸子", "研發(fā)部經(jīng)理", 10000);
IBranch salesDep = new Branch("馬兒拐子", "銷售部經(jīng)理", 20000);
IBranch financeDep = new Branch("趙三駝子", "財(cái)務(wù)部經(jīng)理", 30000);
// 再把三個(gè)小組長(zhǎng)產(chǎn)生出來(lái)
IBranch firstDevGroup = new Branch("楊三乜斜", "開(kāi)發(fā)一組組長(zhǎng)", 5000);
IBranch secondDevGroup = new Branch("吳大棒槌", "開(kāi)發(fā)二組組長(zhǎng)", 6000);
// 剩下的就是我們這些小兵了,就是路人甲秀又,路人乙
ILeaf zhengLaoLiu = new Leaf("鄭老六", "研發(fā)部副總", 20000);
ILeaf a = new Leaf("A", "開(kāi)發(fā)人員", 2000);
ILeaf b = new Leaf("B", "開(kāi)發(fā)人員", 2000);
ILeaf c = new Leaf("C", "開(kāi)發(fā)人員", 2000);
ILeaf d = new Leaf("D", "開(kāi)發(fā)人員", 2000);
ILeaf e = new Leaf("E", "開(kāi)發(fā)人員", 2000);
ILeaf f = new Leaf("F", "開(kāi)發(fā)人員", 2000);
ILeaf g = new Leaf("G", "開(kāi)發(fā)人員", 2000);
ILeaf h = new Leaf("H", "銷售人員", 5000);
ILeaf i = new Leaf("I", "銷售人員", 4000);
ILeaf j = new Leaf("J", "財(cái)務(wù)人員", 5000);
ILeaf k = new Leaf("K", "CEO秘書(shū)", 8000);
// 組裝這棵樹(shù)
// 首先是定義總經(jīng)理下有三個(gè)部門(mén)經(jīng)理
ceo.add(developDep);
ceo.add(salesDep);
ceo.add(financeDep);
// 總經(jīng)理下還有一個(gè)秘書(shū)
ceo.add(k);
// 定義研發(fā)部門(mén)下的結(jié)構(gòu)
developDep.add(firstDevGroup);
developDep.add(secondDevGroup);
// 研發(fā)部經(jīng)理下還有一個(gè)副總
developDep.add(zhengLaoLiu);
// 看看開(kāi)發(fā)兩個(gè)開(kāi)發(fā)小組下有什么
firstDevGroup.add(a);
firstDevGroup.add(b);
firstDevGroup.add(c);
secondDevGroup.add(d);
secondDevGroup.add(e);
secondDevGroup.add(f);
// 再看銷售部下的人員情況
salesDep.add(h);
salesDep.add(i);
// 最后一個(gè)財(cái)務(wù)
financeDep.add(j);
// 樹(shù)狀結(jié)構(gòu)寫(xiě)完畢单寂,然后我們打印出來(lái)
System.out.println(ceo.getInfo());
//打印出來(lái)整個(gè)樹(shù)形
getAllSubordinateInfo(ceo.getSubordinateInfo());
}
// 遍歷所有的樹(shù)枝節(jié)點(diǎn),打印出信息
private static void getAllSubordinateInfo(ArrayList<Object> subordinateList) {
for (Object obj : subordinateList) {
if (obj instanceof Leaf) {
ILeaf leaf = (ILeaf)obj;
System.out.println(leaf.getInfo());
} else {
IBranch branch = (IBranch)obj;
System.out.println(branch.getInfo());
getAllSubordinateInfo(branch.getSubordinateInfo());
}
}
}
}
和我們期望要的結(jié)果一樣吐辙,一棵完整的樹(shù)就生成了宣决,而且我們還能夠遍歷,但這樣的類設(shè)計(jì)是有問(wèn)題的昏苏,getInfo()
每個(gè)接口都有為什么不能抽象出來(lái)尊沸?Root
類和Branch
類有什么差別?為什么要定義成兩個(gè)接口兩個(gè)類贤惯?如果我要加一個(gè)任職期限洼专,是不是每個(gè)類都需要修改?如果我要后序遍歷(從員工找到他的上級(jí)領(lǐng)導(dǎo))能做嗎孵构?
問(wèn)題很多屁商,我們一個(gè)一個(gè)解決,先說(shuō)抽象的問(wèn)題颈墅,確實(shí)可以把IBranch
和IRoot
合并成一個(gè)接口蜡镶,這個(gè)我們先肯定下來(lái)雾袱,這是個(gè)比較大的改動(dòng),我們先畫(huà)個(gè)類圖:
這個(gè)類圖還是有點(diǎn)問(wèn)題的官还,接口的作用是什么芹橡?定義共性,那ILeaf
和IBranch
是不是也有共性呢妻枕?有getInfo()
僻族,我們是不是把這個(gè)共性也已經(jīng)封裝起來(lái),再修改一下類圖:
類圖上有兩個(gè)接口屡谐,ICorp
是公司所有人員的信息的接口類述么,不管你是經(jīng)理還是員工,你都有名字愕掏,職位邢笙,薪水如捅,這個(gè)定義成一個(gè)接口沒(méi)有錯(cuò),IBranch
有沒(méi)有必要呢?我們先實(shí)現(xiàn)出來(lái)然后再說(shuō):
/**
* 公司類档冬,定義每個(gè)員工都有信息
*/
public interface ICorp {
// 獲取信息
String getInfo();
}
public class Leaf implements ICorp {
// 小兵的名字
private String name = "";
// 小兵的職位
private String position = "";
// 小兵的薪水
private int salary = 0;
// 通過(guò)構(gòu)造函數(shù)傳遞信息
public Leaf(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
// 獲得小兵的信息
@Override
public String getInfo() {
return String.format("名稱: %s\t職位: %s\t薪水: %s", this.name, this.position, this.salary);
}
}
/**
* 樹(shù)枝節(jié)點(diǎn)起趾,有下屬節(jié)點(diǎn)
*/
public interface IBranch {
// 能夠增加小兵(樹(shù)葉節(jié)點(diǎn))或者是經(jīng)理(樹(shù)枝節(jié)點(diǎn))
void addSubordinate(ICorp corp);
// 獲取下級(jí)信息
ArrayList<ICorp> getSubordinateInfo();
}
/**
* 樹(shù)枝節(jié)點(diǎn)班巩,就是各個(gè)部門(mén)經(jīng)理和組長(zhǎng)的角色
*/
public class Branch implements IBranch, ICorp {
// 下級(jí)
private ArrayList<ICorp> subordinateList = new ArrayList<>();
//姓名
private String name = "";
// 職位
private String position = "";
// 薪水
private int salary = 0;
// 通過(guò)構(gòu)造函數(shù)傳遞樹(shù)枝節(jié)點(diǎn)的參數(shù)
public Branch(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
// 增加一個(gè)下屬查近,可能是小頭目,也可能是個(gè)小兵
@Override
public void addSubordinate(ICorp corp) {
this.subordinateList.add(corp);
}
@Override
public ArrayList<ICorp> getSubordinateInfo() {
return this.subordinateList;
}
// 獲取自己的信息
@Override
public String getInfo() {
return String.format("名稱: %s\t職位: %s\t薪水: %s", this.name, this.position, this.salary);
}
}
/**
* @author YangYunhe
* @date 2020-12-28 10:20
* @description 組裝樹(shù)形結(jié)構(gòu)
*/
public class Client {
public static void main(String[] args) {
// 組裝一個(gè)組織結(jié)構(gòu)
Branch ceo = compositeCorpTree();
// 打印CEO的信息
System.out.println(ceo.getInfo());
// 打印所有員工的信息
System.out.println(getTreeInfo(ceo));
}
// 遍歷整棵樹(shù)语卤,只要給我根節(jié)點(diǎn)追逮,我就能遍歷出所有的節(jié)點(diǎn)
public static String getTreeInfo(Branch root) {
StringBuilder info = new StringBuilder();
ArrayList<ICorp> subordinateList = root.getSubordinateInfo();
for (ICorp iCorp : subordinateList) {
if(iCorp instanceof Leaf) {
info.append(iCorp.getInfo()).append("\n");
} else {
info.append(iCorp.getInfo()).append("\n").append(getTreeInfo((Branch)iCorp));
}
}
return info.toString();
}
public static Branch compositeCorpTree() {
// 首先產(chǎn)生了CEO
Branch ceo = new Branch("王大麻子", "CEO", 100000);
// 產(chǎn)生三個(gè)部門(mén)經(jīng)理
Branch developDep = new Branch("劉大瘸子", "研發(fā)部經(jīng)理", 10000);
Branch salesDep = new Branch("馬兒拐子", "銷售部經(jīng)理", 20000);
Branch financeDep = new Branch("趙三駝子", "財(cái)務(wù)部經(jīng)理", 30000);
// 再把三個(gè)小組長(zhǎng)產(chǎn)生出來(lái)
Branch firstDevGroup = new Branch("楊三乜斜", "開(kāi)發(fā)一組組長(zhǎng)", 5000);
Branch secondDevGroup = new Branch("吳大棒槌", "開(kāi)發(fā)二組組長(zhǎng)", 6000);
// 把所有的小兵都創(chuàng)建出來(lái)
Leaf zhengLaoLiu = new Leaf("鄭老六", "研發(fā)部副總", 20000);
Leaf a = new Leaf("A", "開(kāi)發(fā)人員", 2000);
Leaf b = new Leaf("B", "開(kāi)發(fā)人員", 2000);
Leaf c = new Leaf("C", "開(kāi)發(fā)人員", 2000);
Leaf d = new Leaf("D", "開(kāi)發(fā)人員", 2000);
Leaf e = new Leaf("E", "開(kāi)發(fā)人員", 2000);
Leaf f = new Leaf("F", "開(kāi)發(fā)人員", 2000);
Leaf g = new Leaf("G", "開(kāi)發(fā)人員", 2000);
Leaf h = new Leaf("H", "銷售人員", 5000);
Leaf i = new Leaf("I", "銷售人員", 4000);
Leaf j = new Leaf("J", "財(cái)務(wù)人員", 5000);
Leaf k = new Leaf("K", "CEO秘書(shū)", 8000);
// 組裝這棵樹(shù)
// 定義CEO下的三個(gè)部門(mén)經(jīng)理和一個(gè)秘書(shū)
ceo.addSubordinate(developDep);
ceo.addSubordinate(salesDep);
ceo.addSubordinate(financeDep);
// 總經(jīng)理下還有一個(gè)秘書(shū)
ceo.addSubordinate(k);
// 定義研發(fā)部門(mén)下的結(jié)構(gòu)
developDep.addSubordinate(firstDevGroup);
developDep.addSubordinate(secondDevGroup);
developDep.addSubordinate(zhengLaoLiu);
// 定義兩個(gè)開(kāi)發(fā)小組下的結(jié)構(gòu)
firstDevGroup.addSubordinate(a);
firstDevGroup.addSubordinate(b);
firstDevGroup.addSubordinate(c);
secondDevGroup.addSubordinate(d);
secondDevGroup.addSubordinate(e);
secondDevGroup.addSubordinate(f);
// 定義銷售部下的人員
salesDep.addSubordinate(h);
salesDep.addSubordinate(i);
// 定義財(cái)務(wù)部下的人員
financeDep.addSubordinate(j);
return ceo;
}
}
我們的程序還可以繼續(xù)優(yōu)化,Leaf
和Branch
中都有getInfo()
方法粹舵,可以抽象出來(lái):
/**
* 公司人員抽象類
*/
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() {
return String.format("名稱: %s\t職位: %s\t薪水: %s", this.name, this.position, this.salary);
}
}
/**
* 普通員工很簡(jiǎn)單钮孵,就寫(xiě)一個(gè)構(gòu)造函數(shù)就可以了
*/
public class Leaf extends Corp {
public Leaf(String name, String position, int salary) {
super(name, position, salary);
}
}
/**
* 節(jié)點(diǎn)類,也簡(jiǎn)單了很多
*/
public class Branch extends Corp {
// 領(lǐng)導(dǎo)下邊有那些下級(jí)領(lǐng)導(dǎo)和小兵
private ArrayList<Corp> subordinateList = new ArrayList<>();
public Branch(String name, String position, int salary) {
super(name, position, salary);
}
// 增加一個(gè)下屬眼滤,可能是小頭目巴席,也可能是個(gè)小兵
public void addSubordinate(Corp corp) {
this.subordinateList.add(corp);
}
// 我有哪些下屬
public ArrayList<Corp> getSubordinateInfo() {
return this.subordinateList;
}
}
public class Client {
public static void main(String[] args) {
// 組裝一個(gè)組織結(jié)構(gòu)
Branch ceo = compositeCorpTree();
// 打印CEO的信息
System.out.println(ceo.getInfo());
// 打印所有員工的信息
System.out.println(getTreeInfo(ceo));
}
// 遍歷整棵樹(shù),只要給我根節(jié)點(diǎn)诅需,我就能遍歷出所有的節(jié)點(diǎn)
public static String getTreeInfo(Branch root) {
StringBuilder info = new StringBuilder();
ArrayList<Corp> subordinateList = root.getSubordinateInfo();
for (Corp corp : subordinateList) {
if(corp instanceof Leaf) {
info.append(corp.getInfo()).append("\n");
} else {
info.append(corp.getInfo()).append("\n").append(getTreeInfo((Branch)corp));
}
}
return info.toString();
}
public static Branch compositeCorpTree() {
// 首先產(chǎn)生了CEO
Branch ceo = new Branch("王大麻子", "CEO", 100000);
// 產(chǎn)生三個(gè)部門(mén)經(jīng)理
Branch developDep = new Branch("劉大瘸子", "研發(fā)部經(jīng)理", 10000);
Branch salesDep = new Branch("馬兒拐子", "銷售部經(jīng)理", 20000);
Branch financeDep = new Branch("趙三駝子", "財(cái)務(wù)部經(jīng)理", 30000);
// 再把三個(gè)小組長(zhǎng)產(chǎn)生出來(lái)
Branch firstDevGroup = new Branch("楊三乜斜", "開(kāi)發(fā)一組組長(zhǎng)", 5000);
Branch secondDevGroup = new Branch("吳大棒槌", "開(kāi)發(fā)二組組長(zhǎng)", 6000);
// 把所有的小兵都創(chuàng)建出來(lái)
Leaf zhengLaoLiu = new Leaf("鄭老六", "研發(fā)部副總", 20000);
Leaf a = new Leaf("A", "開(kāi)發(fā)人員", 2000);
Leaf b = new Leaf("B", "開(kāi)發(fā)人員", 2000);
Leaf c = new Leaf("C", "開(kāi)發(fā)人員", 2000);
Leaf d = new Leaf("D", "開(kāi)發(fā)人員", 2000);
Leaf e = new Leaf("E", "開(kāi)發(fā)人員", 2000);
Leaf f = new Leaf("F", "開(kāi)發(fā)人員", 2000);
Leaf g = new Leaf("G", "開(kāi)發(fā)人員", 2000);
Leaf h = new Leaf("H", "銷售人員", 5000);
Leaf i = new Leaf("I", "銷售人員", 4000);
Leaf j = new Leaf("J", "財(cái)務(wù)人員", 5000);
Leaf k = new Leaf("K", "CEO秘書(shū)", 8000);
// 組裝這棵樹(shù)
// 定義CEO下的三個(gè)部門(mén)經(jīng)理和一個(gè)秘書(shū)
ceo.addSubordinate(developDep);
ceo.addSubordinate(salesDep);
ceo.addSubordinate(financeDep);
// 總經(jīng)理下還有一個(gè)秘書(shū)
ceo.addSubordinate(k);
// 定義研發(fā)部門(mén)下的結(jié)構(gòu)
developDep.addSubordinate(firstDevGroup);
developDep.addSubordinate(secondDevGroup);
developDep.addSubordinate(zhengLaoLiu);
// 定義兩個(gè)開(kāi)發(fā)小組下的結(jié)構(gòu)
firstDevGroup.addSubordinate(a);
firstDevGroup.addSubordinate(b);
firstDevGroup.addSubordinate(c);
secondDevGroup.addSubordinate(d);
secondDevGroup.addSubordinate(e);
secondDevGroup.addSubordinate(f);
// 定義銷售部下的人員
salesDep.addSubordinate(h);
salesDep.addSubordinate(i);
// 定義財(cái)務(wù)部下的人員
financeDep.addSubordinate(j);
return ceo;
}
}
經(jīng)過(guò)這樣一步步的改造漾唉,類、接口減少了很多堰塌,而且程序也簡(jiǎn)單了很多赵刑。
上面我們講到的就是組合模式(也叫合成模式),有時(shí)又叫做部分-整體模式(Part-Whole)蔫仙,主要是用來(lái)描述整體與部分的關(guān)系料睛,用的最多的地方就是樹(shù)形結(jié)構(gòu)丐箩。組合模式通用類圖如下:
我們先來(lái)說(shuō)說(shuō)組合模式的幾個(gè)角色:
抽象構(gòu)件角色(Component):定義參加組合的對(duì)象的共有方法和屬性摇邦,可以定義一些默認(rèn)的行為或?qū)傩孕羯罚槐热缥覀兝又械?code>getInfo() 就封裝到了抽象類中。
葉子構(gòu)件(Leaf):葉子對(duì)象施籍,其下再也沒(méi)有其他的分支居扒。
樹(shù)枝構(gòu)件(Composite):樹(shù)枝對(duì)象,它的作用是組合樹(shù)枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)丑慎;
組合模式有兩種模式喜喂,透明模式和安全模式,這兩個(gè)模式有什么區(qū)別呢竿裂?先看類圖:
這兩種模式各有優(yōu)缺點(diǎn)玉吁,透明模式是把用來(lái)組合使用的方法放到抽象類中,比如add()
腻异、remove()
以及getChildren()
等方法进副,(順便說(shuō)一下,getChildren()
一般返回的結(jié)果為Iterable
的實(shí)現(xiàn)類)悔常,不管葉子對(duì)象還是樹(shù)枝對(duì)象都有相同的結(jié)構(gòu)影斑,通過(guò)判斷getChildren
的返回值確認(rèn)是葉子節(jié)點(diǎn)還是樹(shù)枝節(jié)點(diǎn),如果處理不當(dāng)机打,這個(gè)會(huì)在運(yùn)行期出現(xiàn)問(wèn)題矫户,不建議使用這種方式;安全模式把樹(shù)枝節(jié)點(diǎn)和樹(shù)葉節(jié)點(diǎn)徹底分開(kāi)残邀,樹(shù)枝節(jié)點(diǎn)單獨(dú)擁有用來(lái)組合的方法皆辽,這種方法比較安全,我們的例子使用了安全模式罐旗。
組合模式的優(yōu)缺點(diǎn):
只要是樹(shù)形結(jié)構(gòu)膳汪,就要考慮使用組合模式,只要是要體現(xiàn)局部和整體的關(guān)系的時(shí)候九秀,而且這種關(guān)系還可能比較深遗嗽,就可以考慮使用組合模式吧
-
組合模式有一個(gè)非常明顯的缺點(diǎn),在
Client
類中的的定義樹(shù)葉和樹(shù)枝使用時(shí)使用了如下代碼:Branch developDep = new Branch("劉大瘸子","研發(fā)部門(mén)經(jīng)理",10000); Leaf g = new Leaf("g","開(kāi)發(fā)人員",2000);
直接使用了實(shí)現(xiàn)類去創(chuàng)建對(duì)象鼓蜒,這個(gè)在面向接口編程上是很不恰當(dāng)?shù)摹?/p>
我們?cè)谏厦嬉策€提到了一個(gè)問(wèn)題痹换,就是樹(shù)的遍歷問(wèn)題,從上到下遍歷沒(méi)有問(wèn)題都弹,但是我要是從下往上遍歷呢娇豫?比如在人力資源這顆樹(shù)上,我從中抽取一個(gè)用戶畅厢,要找到它的上級(jí)有哪些冯痢,下級(jí)有哪些,怎么處理?先看類圖:
看類圖中的紅色方框浦楣,只要增加兩個(gè)方法就可以了袖肥,一個(gè)是設(shè)置父節(jié)點(diǎn)是誰(shuí),一個(gè)是查找父節(jié)點(diǎn)是誰(shuí)振劳,我們來(lái)看一下程序的改變:
/**
* 公司人員抽象類
*/
public abstract class Corp {
// 姓名
private String name = "";
// 職位
private String position = "";
// 薪水
private int salary = 0;
// 父節(jié)點(diǎn)
private Corp parent = null;
public Corp(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
public String getInfo() {
return String.format("名稱: %s\t職位: %s\t薪水: %s", this.name, this.position, this.salary);
}
/**
* 增加了以下兩個(gè)方法
*/
// 設(shè)置父節(jié)點(diǎn)
protected void setParent(Corp parent) {
this.parent = parent;
}
// 得到父節(jié)點(diǎn)
public Corp getParent() {
return this.parent;
}
}
/**
* 節(jié)點(diǎn)類
*/
public class Branch extends Corp {
// 領(lǐng)導(dǎo)下邊有那些下級(jí)領(lǐng)導(dǎo)和小兵
private ArrayList<Corp> subordinateList = new ArrayList<>();
public Branch(String name, String position, int salary) {
super(name, position, salary);
}
// 增加一個(gè)下屬椎组,可能是小頭目,也可能是個(gè)小兵
public void addSubordinate(Corp corp) {
// 重要的是這行历恐,添加下屬的時(shí)候給下屬設(shè)置父節(jié)點(diǎn)為自己
corp.setParent(this);
this.subordinateList.add(corp);
}
// 我有哪些下屬
public ArrayList<Corp> getSubordinateInfo() {
return this.subordinateList;
}
}
每個(gè)節(jié)點(diǎn)無(wú)論是樹(shù)枝節(jié)點(diǎn)還是樹(shù)葉節(jié)點(diǎn)寸癌,都增加了一個(gè)屬性:父節(jié)點(diǎn)對(duì)象,這樣在樹(shù)枝節(jié)點(diǎn)增加子節(jié)點(diǎn)或葉子的時(shí)候設(shè)置父節(jié)點(diǎn)弱贼,然后整棵樹(shù)除了根節(jié)點(diǎn)外每個(gè)
節(jié)點(diǎn)都一個(gè)父節(jié)點(diǎn)蒸苇,這樣每個(gè)節(jié)點(diǎn)上都有父節(jié)點(diǎn)了,有了這個(gè)parent
屬性吮旅,后序遍歷(從下往上找)填渠、中序遍歷(從中間某個(gè)環(huán)節(jié)往上或往下遍歷)都解決了,這個(gè)就不多說(shuō)了鸟辅。再提一個(gè)擴(kuò)展問(wèn)題氛什,樹(shù)葉節(jié)點(diǎn)和樹(shù)枝節(jié)點(diǎn)是有順序的,比如我們上面的例子匪凉,研發(fā)一組下邊有三個(gè)成員枪眉,這三個(gè)成員是要進(jìn)行排序的,這種情況怎么處理再层?
本文原書(shū):
《您的設(shè)計(jì)模式》 作者:CBF4LIFE