JAVA设计模之组合模式

2019-04-13 17:15发布

公司的人事管理就是一个典型的树状结构,你想想你公司的结构是不是这样:
类图:
Leaf和Branch中都有getInfo 信息,可以抽象一个Corp抽象类 那我们先来看抽象类: package com.cbf4life.perfect; /** * 定义一个公司的人员的抽象类 */ @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 + " 职位:" + this.position; info = info + " 薪水:" + this.salary; return info; } }
抽象类嘛,就应该抽象出一些共性的东西出来,然后看两个具体的实现类: package com.cbf4life.perfect; /** * 普通员工很简单,就写一个构造函数就可以了 */ 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; /** * 节点类,也简单了很多 */ public class Branch extends Corp { // 领导下边有那些下级领导和小兵 ArrayList subordinateList = new ArrayList(); // 构造函数是必须的了 public Branch(String _name, String _position, int _salary) { super(_name, _position, _salary); } // 增加一个下属,可能是小头目,也可能是个小兵 public void addSubordinate(Corp corp) { this.subordinateList.add(corp); } // 我有哪些下属 public ArrayList getSubordinate() { return this.subordinateList; } }
再看Client.java 程序 package com.cbf4life.perfect; import java.util.ArrayList; /** * 组装这个树形结构,并展示出来 */ @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 subordinateList = root.getSubordinate(); String info = ""; for (Corp s : subordinateList) { if (s instanceof Leaf) { // 是员工就直接获得信息 info = info + s.getInfo() + " "; } else { // 是个小头目 info = info + s.getInfo() + " " + getTreeInfo((Branch) s); } } return info; } }
你要知道在项目中使用数据库来存储这些信息的,你http://write.blog.csdn.net/postedit?type=edit
从数据库中提出出来哪些人要分配到树枝,哪些人要分配到树叶,树枝与树枝、树叶的关系,这些都需要
人去定义,通常这里使用一个界面去配置,在数据库中是一个标志信息,例如定义这样一张表:
主键 唯一编码 名称 是否是叶子节点 父节点 1 CEO 总经理 否   2 developDep 研发部经理 否 CEO 3 salesDep 销售部经理 否 CEO 4 financeDep 财务部经理 否 CEO 5 k 总经理秘书 否 CEO 6 a 员工A 是 developDep 7 b 员工B 是 developDep
从这张表中已经定义个一个树形结构,我们要做的就是从数据库中读取出来,然后展现到前台上,这
个读取就用个for 循环加上递归是不是就可以把一棵树建立起来?我们程序中其实还包涵了数据的读取和
加工,用了数据库后,数据和逻辑已经在表中定义好了,我们直接读取放到树上就可以了,这个还是比较
容易做了的,大家不妨自己考虑一下。
      上面我们讲到的就是组合模式(也叫合成模式),有时又叫做部分-整体模式(Part-Whole),主要是
用来描述整体与部分的关系,用的最多的地方就是树形结构。组合模式通用类图如下: 我们先来说说组合模式的几个角 {MOD}:
抽象构件角 {MOD}(Component):定义参加组合的对象的共有方法和属性,可以定义一些默认的行为或属性;
比如我们例子中的getInfo 就封装到了抽象类中。
叶子构件(Leaf):叶子对象,其下再也没有其他的分支。
树枝构件(Composite):树枝对象,它的作用是组合树枝节点和叶子节点;
组合模式有两种模式,透明模式和安全模式,这两个模式有什么区别呢?先看类图: 从类图上大家应该能看清楚了,这两种模式各有优缺点,透明模式是把用来组合使用的方法放到抽象
类中,比如add(),remove()以及getChildren 等方法(顺便说一下,getChildren 一般返回的结果为Iterable
的实现类,很多,大家可以看JDK 的帮助),不管叶子对象还是树枝对象都有相同的结构,通过判断是
getChildren 的返回值确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题的,不是很
建议的方式;安全模式就不同了,它是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方
法,这种方法比较安全,我们的例子使用了安全模式。
组合模式的优点有哪些呢?第一个优点只要是树形结构,就要考虑使用组合模式,这个一定记住,只
要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式吧。组合模式有一
个非常明显的缺点,看到我们在Client.java 中的的定义了树叶和树枝使用时的定义了吗?如下: Branch developDep = new Branch("刘大瘸子","研发部门经理",10000);

Leaf g = new Leaf("g","开发人员",2000);
发现什么问题了吗?直接使用了实现类!这个在面向接口编程上是很不恰当的,这个在使用的时候要考虑清
楚。 我们在上面也还提到了一个问题,就是树的遍历问题,从上到下遍历没有问题,但是我要是从下往上
遍历呢?比如在人力资源这颗树上,我从中抽取一个用户,要找到它的上级有哪些,下级有哪些,怎么处
理?想想,~~~,再想想!想出来了吧,我们对下答案,先看类图:
看类图中的红 {MOD}方框,只要增加两个方法就可以了,一个是设置父节点是谁,一个是查找父节点是谁,
我们来看一下程序的改变: package com.cbf4life.perfect; /** * 定义一个公司的人员的抽象类 */ @SuppressWarnings("all") public abstract class Corp { // 公司每个人都有名称 private String name = ""; // 公司每个人都职位 private String position = ""; // 公司每个人都有薪水 private int salary = 0; //-------黄 {MOD}部分-------- // 父节点是谁 private Corp parent = null; //--------------------- /* * 通过接口的方式传递,我们改变一下习惯,传递进来的参数名以下划线开始 这个在一些开源项目中非常常见,一般构造函数都是这么定义的 */ 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 + " 职位:" + this.position; info = info + " 薪水:" + this.salary; return info; } //-------黄 {MOD}部分-------- // 设置父节点 protected void setParent(Corp _parent) { this.parent = _parent; } // 得到父节点 public Corp getParent() { return this.parent; } //--------------------- }就增加了黄 {MOD}部分,然后我们再来看看Branch.java 的改变: package com.cbf4life.perfect; import java.util.ArrayList; /** * 节点类,也简单了很多 */ public class Branch extends Corp { // 领导下边有那些下级领导和小兵 ArrayList subordinateList = new ArrayList(); // 构造函数是必须的了 public Branch(String _name, String _position, int _salary) { super(_name, _position, _salary); } // 增加一个下属,可能是小头目,也可能是个小兵 public void addSubordinate(Corp corp) { //--------------------- corp.setParent(this); // 设置父节点 //-------黄 {MOD}部分-------- this.subordinateList.add(corp); } // 我有哪些下属 public ArrayList getSubordinate() { return this.subordinateList; } } 增加了黄 {MOD}部分,看懂程序了吗?就是在每个节点甭管是树枝节点还是树叶节点,都增加了一个属性:
父节点对象,这样在树枝节点增加子节点或叶子的时候设置父节点,然后你看整棵树就除了根节点外每个
节点都一个父节点,剩下的事情还不好处理吗?每个节点上都有父节点了,你要往上找,那就找呗!Client
程序我就不写了,今天已经拷贝的代码实在有点多,大家自己考虑一下,写个find 方法,然后一个一个往
上找,最简单的方法了!
有了这个parent 属性,什么后序遍历(从下往上找)、中序遍历(从中间某个环节往上或往下遍历)
都解决了,这个就不多说了。
再提一个问题,树叶节点和树枝节点是有顺序的,你不能乱排的,怎么办?比如我们上面的例子,研
发一组下边有三个成员,这三个成员是要进行排序的呀,你怎么处理?问我呀,问你呢,好好想想,以后
用到着的!