设计模式--组合模式

来源:互联网 发布:数据采集费发票 编辑:程序博客网 时间:2024/04/27 20:45
组合模式【Composite Pattern】
大家在上学的时候应该都学过“数据结构”这门课程吧,还记得其中有一节叫“二叉树”
吧,我们上学那会儿这一章节是必考内容,左子树,右子树,什么先序遍历后序遍历什么,
重点就是二叉树的的遍历,我还记得当时老师就说,考试的时候一定有二叉树的构建和遍历,
现在想起来还是觉的老师是正确的,树状结果在实际项目应用的非常广泛。
咱就先说个最常见的例子,公司的人事管理就是一个典型的树状结构,你想想你公司的
结构是不是这样:
总经理
王大麻子
销售部经理
马二拐子
财务部经理
赵三驼子
研发部经理
刘大瘸子
研发二组组长
吴大棒槌
研发一组组长
杨三乜斜
研发部副经理
郑老六
开发人员
员工A
开发人员
员工B
开发人员
员工C
开发人员
员工E
开发人员
员工D
开发人员
员工F
销售人员
员工I
销售人员
员工H
财务人员
员工J
秘书
员工K
从最高的老大,往下一层一层的管理,最后到我们这层小兵,很典型的树状结构(说明
一下,这不是二叉树,有关二叉树的定义可以翻翻以前的教科书),我们今天的任务就是要
把这个树状结构实现出来,并且还要把它遍历一遍,你要确认你建立的树是否有问题呀。
从这个树状结构上分析,有两种节点:有分支的节点(如研发部经理)和无分支的节点
(如员工A、员工D 等),我们增加一点学术术语上去,总经理叫做根节点(是不是想到XML
中的那个根节点root,那就对了),类似研发部经理有分支的节点叫做树枝节点,类似员工
A 的无分支的节点叫做树叶节点,都很形象,三个类型的的节点,那是不是定义三个类就可
以?好,我们按照这个思路走下去,先看我们自己设计的类图:
您的设计模式
第 152 页
这个类图是初学者最容易想到的类图(如果你已经看明白这个类图的缺陷了,就可以不
看下边的实现了,我是循序渐进的讲课,呵呵),我那来看这个实现:
先看最高级别的根节点的实现:
package com.cbf4life.common;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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();
}
您的设计模式
第 153 页
这个根节点就是我们的总经理CEO,然后看实现类:
package com.cbf4life.common;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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;;
您的设计模式
第 154 页
info = info + "\t职位:" + this.position;
info = info + "\t薪水: " + this.salary;
return info;
}
//得到下级的信息
public ArrayList getSubordinateInfo() {
return this.subordinateList;
}
}
很简单,通过构造函数传入参数,然后获得信息,还可以增加子树枝节点(部门经理)
和叶子节点(秘书)。我们再来看IBranch.java:
package com.cbf4life.common;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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();
}
下面是树枝节点的实现类:
您的设计模式
第 155 页
package com.cbf4life.common;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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;
您的设计模式
第 156 页
return info;
}
//获得下级的信息
public ArrayList getSubordinateInfo() {
return this.subordinateList;
}
}
最后看叶子节点,也就是员工的接口:
package com.cbf4life.common;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 叶子节点,也就是最小的小兵了,只能自己干活,不能指派别人了
*/
public interface ILeaf {
//获得自己的信息呀
public String getInfo();
}
下面是叶子节点的实现类:
package com.cbf4life.common;
/**
* @author cbf4Life cbf4life@126.com
* 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;
您的设计模式
第 157 页
//通过构造函数传递信息
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.cbf4life.common;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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);
您的设计模式
第 158 页
//再把三个小组长产生出来
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);
//再看销售部下的人员情况
您的设计模式
第 159 页
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
您的设计模式
第 160 页
名称: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 是不是
您的设计模式
第 161 页
也有共性呢?有getInfo(),我们是不是要把这个共性也已经封装起来呢?好,我们再修
改一下类图:
类图上有两个接口,ICorp 是公司所有人员的信息的接口类,不管你是经理还是员工,
你都有名字,职位,薪水,这个定义成一个接口没有错,IBranch 有没有必要呢?我们先实
现出来然后再说。
先看ICorp.java 源代码:
package com.cbf4life.advance;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 公司类,定义每个员工都有信息
*/
public interface ICorp {
//每个员工都有信息,你想隐藏,门儿都没有!
public String getInfo();
}
接口很简单,只有一个方法,就是获得员工的信息,我们再来看实现类:
您的设计模式
第 162 页
package com.cbf4life.advance;
/**
* @author cbf4Life cbf4life@126.com
* 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.cbf4life.advance;
import java.util.ArrayList;
/**
您的设计模式
第 163 页
* @author cbf4Life cbf4life@126.com
* 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.cbf4life.advance;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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;
您的设计模式
第 164 页
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.cbf4life.advance;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 组装这个树形结构,并展示出来
*/
@SuppressWarnings("all")
public class Client {
public static void main(String[] args) {
//首先是组装一个组织结构出来
您的设计模式
第 165 页
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);
您的设计模式
第 166 页
//研发部经理
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;
}
}
您的设计模式
第 167 页
运行结果如下:
姓名:王大麻子职位:总经理薪水: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
接口也没有了,直接把方法放到了实现类中了,那我们先来看抽象类:
您的设计模式
第 168 页
package com.cbf4life.perfect;
/**
* @author cbf4Life cbf4life@126.com
* 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.cbf4life.perfect;
/**
* @author cbf4Life cbf4life@126.com
* I'm glad to share my knowledge with you all.
* 普通员工很简单,就写一个构造函数就可以了
您的设计模式
第 169 页
*/
public class Leaf extends Corp {
//就写一个构造函数,这个是必须的
public Leaf(String _name,String _position,int _salary){
super(_name,_position,_salary);
}
}
这个改动比较多,就几行代码就完成了,确实就应该这样,下面是小头目的实现类:
package com.cbf4life.perfect;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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 程序,这个就没有多大变化了:
您的设计模式
第 170 页
package com.cbf4life.perfect;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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);
您的设计模式
第 171 页
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){
您的设计模式
第 172 页
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 程序并没有改变多少呀,非常正确,树的组装你是跑不了的,你要知道在项目中使
您的设计模式
第 173 页
用数据库来存储这些信息的,你从数据库中提出出来哪些人要分配到树枝,哪些人要分配到
树叶,树枝与树枝、树叶的关系,这些都需要人去定义,通常这里使用一个界面去配置,在
数据库中是一个标志信息,例如定义这样一张表:
主键 编号 名称 是否是叶子节点 父节点
1 CEO 总经理 否
2 developDep 研发部经理 否 CEO
3 salesDep 销售部经理 否 CEO
4 financeDep 财务部经理 否 CEO
5 k 总经理秘书 是 CEO
6 a 员工A 是 developed
7 b 员工B 是 Developed
从这张表中已经定义个一个树形结构,我们要做的就是从数据库中读取出来,然后展现
到前台上,这个读取就用个for 循环加上递归是不是就可以把一棵树建立起来?我们程序中
其实还包涵了数据的读取和加工,用了数据库后,数据和逻辑已经在表中定义好了,我们直
接读取放到树上就可以了,这个还是比较容易做了的,大家不妨自己考虑一下。
上面我们讲到的就是组合模式(也叫合成模式),有时又叫做部分-整体模式
(Part-Whole),主要是用来描述整体与部分的关系,用的最多的地方就是树形结构。组合
模式通用类图如下:
我们先来说说组合模式的几个角色:
抽象构件角色(Component):定义参加组合的对象的共有方法和属性,可以定义一些默
您的设计模式
第 174 页
认的行为或属性;比如我们例子中的getInfo 就封装到了抽象类中。
叶子构件(Leaf):叶子对象,其下再也没有其他的分支。
树枝构件(Composite):树枝对象,它的作用是组合树枝节点和叶子节点;
组合模式有两种模式,透明模式和安全模式,这两个模式有什么区别呢?先看类图:
透明模式类图
安全模式类图
从类图上大家应该能看清楚了,这两种模式各有优缺点,透明模式是把用来组合使用的
方法放到抽象类中,比如add(),remove()以及getChildren 等方法(顺便说一下,
getChildren 一般返回的结果为Iterable 的实现类,很多,大家可以看JDK 的帮助),不管
叶子对象还是树枝对象都有相同的结构,通过判断是getChildren 的返回值确认是叶子节点
还是树枝节点,如果处理不当,这个会在运行期出现问题的,不是很建议的方式;安全模式
您的设计模式
第 175 页
就不同了,它是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种
方法比较安全,我们的例子使用了安全模式。
组合模式的优点有哪些呢?第一个优点只要是树形结构,就要考虑使用组合模式,这个
一定记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下
组合模式吧。组合模式有一个非常明显的缺点,看到我们在Client.java 中的的定义了树叶
和树枝使用时的定义了吗?如下:
Branch developDep = new Branch("刘大瘸子","研发部门经理",10000);

Leaf g = new Leaf("g","开发人员",2000);
发现什么问题了吗?直接使用了实现类!这个在面向接口编程上是很不恰当的,这个在使用
的时候要考虑清楚。
组合模式在项目中到处都有,比如现在的页面结构一般都是上下结构,上面放系统的
Logo,下边分为两部分:左边是导航菜单,右边是展示区,左边的导航菜单一般都是树形的
结构,比较清晰,这个JavaScript 有很多例子,大家可以到网上搜索一把;还有,我们的
自己也是一个树状结构,根据我,能够找到我的父母,根据父亲又能找到爷爷奶奶,根据母
亲能够找到外公外婆等等,很典型的树形结构,而且还很规范(这个要是不规范那肯定是乱
套了)。
我们在上面也还提到了一个问题,就是树的遍历问题,从上到下遍历没有问题,但是我
要是从下往上遍历呢?比如在人力资源这颗树上,我从中抽取一个用户,要找到它的上级有
哪些,下级有哪些,怎么处理?想想,~~~,再想想!想出来了吧,我们对下答案,先看类
图:
您的设计模式
第 176 页
看类图中的红色方框,只要增加两个方法就可以了,一个是设置父节点是谁,一个是查
找父节点是谁,我们来看一下程序的改变:
package com.cbf4life.extend;
/**
* @author cbf4Life cbf4life@126.com
* 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;
//父节点是谁
private Corp parent = null;
/*通过接口的方式传递,我们改变一下习惯,传递进来的参数名以下划线开始
* 这个在一些开源项目中非常常见,一般构造函数都是定义的
*/
public Corp(String _name,String _position,int _salary){
this.name = _name;
this.position = _position;
this.salary = _salary;
}
您的设计模式
第 177 页
//获得员工信息
public String getInfo(){
String info = "";
info = "姓名:" + this.name;
info = info + "\t职位:"+ this.position;
info = info + "\t薪水:" + this.salary;
return info;
}
//设置父节点
protected void setParent(Corp _parent){
this.parent = _parent;
}
//得到父节点
public Corp getParent(){
return this.parent;
}
}
就增加了黄色部分,然后我们再来看看Branch.java 的改变:
package com.cbf4life.extend;
import java.util.ArrayList;
/**
* @author cbf4Life cbf4life@126.com
* 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) {
corp.setParent(this); //设置父节点
您的设计模式
第 178 页
this.subordinateList.add(corp);
}
//我有哪些下属
public ArrayList<Corp> getSubordinate() {
return this.subordinateList;
}
}
增加了黄色部分,看懂程序了吗?就是在每个节点甭管是树枝节点还是树叶节点,都增
加了一个属性:父节点对象,这样在树枝节点增加子节点或叶子的时候设置父节点,然后你
看整棵树就除了根节点外每个节点都一个父节点,剩下的事情还不好处理吗?每个节点上都
有父节点了,你要往上找,那就找呗!Client 程序我就不写了,今天已经拷贝的代码实在
有点多,大家自己考虑一下,写个find 方法,然后一个一个往上找,最简单的方法了!
有了这个parent 属性,什么后序遍历(从下往上找)、中序遍历(从中间某个环节往上
或往下遍历)都解决了,这个就不多说了。
再提一个问题,树叶节点和树枝节点是有顺序的,你不能乱排的,怎么办?比如我们上
面的例子,研发一组下边有三个成员,这三个成员是要进行排序的呀,你怎么处理?问我呀,
问你呢,好好想想,以后用到着的!