大家在上学的时候应该都学过“数据结构”这门课程吧,还记得其中有一节叫“二叉树”吧,我们上学那会儿这一章节是必考内容,左子树,右子树,什么先序遍历后序遍历什么,重点就是二叉树的的遍历,我还记得当时老师就说,考试的时候一定有二叉树的构建和遍历,现在想起来还是觉的老师是正确的,树状结果在实际项目应用的非常广泛。

  咱就先说个最常见的例子,公司的人事管理就是一个典型的树状结构,你想想你公司的结构是不是这样:

  从最高的老大,往下一层一层的管理,最后到我们这层小兵,很典型的树状结构(说明一下,这不是二叉树,有关二叉树的定义可以翻翻以前的教科书),我们今天的任务就是要把这个树状结构实现出来,并且还要把它遍历一遍,你要确认你建立的树是否有问题呀。

  从这个树状结构上分析,有两种节点:有分支的节点(如研发部经理)和无分支的节点(如员工 A、员工 D 等),我们增加一点学术术语上去,总经理叫做根节点(是不是想到 XML 的那个根节点 root,那就对了),类似研发部经理有分支的节点叫做树枝节点,类似员工 A 的无分支的节点叫做树叶节点,都很形象,三个类型的的节点,那是不是定义三个类就可以?好,我们按照这个思路走下去,先看我们自己设计的类图:

  这个类图是初学者最容易想到的类图(如果你已经看明白这个类图的缺陷了,就可以不看下边的实现了,我是循序渐进的讲课,呵呵),那我们来看这个实现:

  先看最高级别的根节点的实现:

  1. package com.pattern.composite;
  2.  
  3. import java.util.ArrayList;
  4.  
  5. /**
  6. * 定义一个根节点,就为总经理服务
  7. * @author http://www.cnblogs.com/initial-road/
  8. *
  9. */
  10. public interface IRoot {
  11.  
  12. // 得到总经理的信息
  13. public String getInfo();
  14.  
  15. // 总经理下边要有小兵,那要能增加小兵,比如研发总经理,这是个树枝节点
  16. public void add(IBranch branch);
  17.  
  18. // 那要能增加树叶节点
  19. public void add(ILeaf leaf);
  20.  
  21. // 既然能增加,那要还要能够便遍历,不肯能总经理不知道他手下有哪些人
  22. public ArrayList getSubordinateInfo();
  23.  
  24. }
  25.  
  26. // 这个根节点就是我们的总经理 CEO,然后看实现类:
  27.  
  28. package com.pattern.composite;
  29.  
  30. import java.util.ArrayList;
  31.  
  32. /**
  33. * 根节点的实现类
  34. * @author http://www.cnblogs.com/initial-road/
  35. *
  36. */
  37. @SuppressWarnings("unchecked")
  38. public class Root implements IRoot {
  39. // 保存根节点下的树枝节点和树叶节点,Subrdinate的意思是下级
  40. private ArrayList subordinateList = new ArrayList();
  41.  
  42. // 根节点的名称
  43. private String name = "";
  44.  
  45. // 根节点的职位
  46. private String position = "";
  47.  
  48. // 根节点的薪水
  49. private int salary = 0;
  50.  
  51. // 通过构造函数传递进来总经理的信息
  52. public Root(String name, String position, int salary){
  53. this.name = name;
  54. this.position = position;
  55. this.salary = salary;
  56. }
  57.  
  58. // 增加树枝节点
  59. public void add(IBranch branch) {
  60. this.subordinateList.add(branch);
  61. }
  62.  
  63. // 增加叶子节点,比如秘书,直接属于总经理
  64. public void add(ILeaf leaf) {
  65. this.subordinateList.add(leaf);
  66. }
  67.  
  68. // 得知自己的信息
  69. public String getInfo() {
  70. String info = "";
  71. info = "名称:" + this.name;
  72. info = info + "\t职位:" + this.position;
  73. info = info + "\t薪水:" + this.salary;
  74. return info;
  75. }
  76.  
  77. // 得到下级的信息
  78. public ArrayList getSubordinateInfo() {
  79. return this.subordinateList;
  80. }
  81.  
  82. }

  很简单,通过构造函数传入参数,然后获得信息,还可以增加子树枝节点(部门经理)和叶子节点(秘书)。我们再来看 IBranch.java:

  1. package com.pattern.composite;
  2.  
  3. import java.util.ArrayList;
  4.  
  5. /**
  6. * 树枝节点,也就是各个部门经理和组长的角色
  7. * @author http://www.cnblogs.com/initial-road/
  8. *
  9. */
  10. @SuppressWarnings("unchecked")
  11. public interface IBranch {
  12.  
  13. // 获得信息
  14. public String getInfo();
  15.  
  16. // 增加数据节点,例如研发部下的研发一组
  17. public void add(IBranch branch);
  18.  
  19. // 增加叶子节点
  20. public void add(ILeaf leaf);
  21.  
  22. // 获得下级信息
  23. public ArrayList getSubordinateInfo();
  24.  
  25. }
  26.  
  27. // 下面是树枝节点的实现类:
  28. package com.pattern.composite;
  29.  
  30. import java.util.ArrayList;
  31.  
  32. /**
  33. * 所有的树枝节点
  34. * @author http://www.cnblogs.com/initial-road/
  35. *
  36. */
  37. @SuppressWarnings("unchecked")
  38. public class Branch implements IBranch {
  39. // 存储子节点信息
  40. private ArrayList subordinateList = new ArrayList();
  41.  
  42. // 树枝节点的名称
  43. private String name = "";
  44.  
  45. // 树枝节点的职位
  46. private String position = "";
  47.  
  48. // 树枝节点的薪水
  49. private int salary = 0;
  50.  
  51. // 通过构造函数传递树枝节点的参数
  52. public Branch(String name, String position, int salary){
  53. this.name = name;
  54. this.position = position;
  55. this.salary = salary;
  56. }
  57.  
  58. // 增加一个子树枝节点
  59. public void add(IBranch branch) {
  60. this.subordinateList.add(branch);
  61. }
  62.  
  63. // 增加一个叶子节点
  64. public void add(ILeaf leaf) {
  65. this.subordinateList.add(leaf);
  66. }
  67.  
  68. // 获得自己树枝节点的信息
  69. public String getInfo() {
  70. String info = "";
  71. info = "名称:" + this.name;
  72. info = info + "\t职位:" + this.position;
  73. info = info + "\t薪水:" + this.salary;
  74. return info;
  75. }
  76.  
  77. // 获得下级的信息
  78. public ArrayList getSubordinateInfo() {
  79. return this.subordinateList;
  80. }
  81.  
  82. }
  83.  
  84. // 最后看叶子节点,也就是员工的接口:
  85.  
  86. package com.pattern.composite;
  87.  
  88. /**
  89. * 叶子节点,也就是最小的小兵了,只能自己干活,不能指派别人了
  90. * @author http://www.cnblogs.com/initial-road/
  91. *
  92. */
  93. public interface ILeaf {
  94.  
  95. // 获得自己的信息呀
  96. public String getInfo();
  97.  
  98. }
  99.  
  100. // 下面是叶子节点的实现类:
  101.  
  102. package com.pattern.composite;
  103.  
  104. /**
  105. * 最小的叶子节点
  106. * @author http://www.cnblogs.com/initial-road/
  107. *
  108. */
  109. public class Leaf implements ILeaf {
  110. // 叶子叫什么名字
  111. private String name = "";
  112.  
  113. // 叶子的职位
  114. private String position = "";
  115.  
  116. // 叶子的薪水
  117. private int salary = 0;
  118.  
  119. // 通过构造函数传递信息
  120. public Leaf(String name, String position, int salary){
  121. this.name = name;
  122. this.position = position;
  123. this.salary = salary;
  124. }
  125.  
  126. // 最小的小兵只能获得自己的信息了
  127. public String getInfo() {
  128. String info = "";
  129. info = "名称:" + this.name;
  130. info = info + "\t职位:" + this.position;
  131. info = info + "\t薪水:" + this.salary;
  132. return info;
  133. }
  134.  
  135. }

  好了,所有的根节点,树枝节点和叶子节点都已经实现了,从总经理、部门经理到最终的员工都已经实现了,然后的工作就是组装成一个树状结构和遍历这个树状结构,看 Client.java 程序:

  1. package com.pattern.composite;
  2.  
  3. import java.util.ArrayList;
  4.  
  5. /**
  6. * Client的作用是组装这棵树,并遍历一遍
  7. * @author http://www.cnblogs.com/initial-road/
  8. *
  9. */
  10. @SuppressWarnings("unchecked")
  11. public class Client {
  12.  
  13. public static void main(String[] args) {
  14.  
  15. // 首先产生了一个根节点
  16. IRoot ceo = new Root("张三", "总经理", 100000);
  17.  
  18. // 差生三个部门经理,也就是树枝节点
  19. IBranch developDep = new Branch("李四", "研发部门经理", 10000);
  20. IBranch salesDep = new Branch("王五", "销售部门经理", 20000);
  21. IBranch financeDep = new Branch("赵六", "财务部门经理", 30000);
  22.  
  23. // 再把三个小组长产生出来
  24. IBranch firstDevGroup = new Branch("杨三", "开发一组组长", 5000);
  25. IBranch secondDevGroup = new Branch("吴大", "开发二组组长", 6000);
  26.  
  27. // 剩下的就是我们这些小兵了,就是路人甲,路人乙
  28. ILeaf a = new Leaf("a", "开发人员", 2000);
  29. ILeaf b = new Leaf("b", "开发人员", 2000);
  30. ILeaf c = new Leaf("c", "开发人员", 2000);
  31. ILeaf d = new Leaf("d", "开发人员", 2000);
  32. ILeaf e = new Leaf("e", "开发人员", 2000);
  33. ILeaf f = new Leaf("f", "开发人员", 2000);
  34. ILeaf g = new Leaf("g", "开发人员", 2000);
  35. ILeaf h = new Leaf("h", "开发人员", 5000);
  36. ILeaf i = new Leaf("i", "开发人员", 4000);
  37. ILeaf j = new Leaf("j", "开发人员", 5000);
  38. ILeaf k = new Leaf("k", "开发人员", 8000);
  39. ILeaf zhengLaoLiu = new Leaf("郑老六", "研发部副总", 20000);
  40.  
  41. // 该产生的人都产生出来了,然后我们怎么组装这棵树
  42. // 首先是定义总经理下有三个部门经理
  43. ceo.add(developDep);
  44. ceo.add(salesDep);
  45. ceo.add(financeDep);
  46. //总经理下还有一个秘书
  47. ceo.add(k);
  48.  
  49. // 定义研发部门下的结构
  50. developDep.add(firstDevGroup);
  51. developDep.add(secondDevGroup);
  52.  
  53. // 研发部经理下还有一个副总
  54. developDep.add(zhengLaoLiu);
  55.  
  56. // 看看开发两个小组下有什么
  57. firstDevGroup.add(a);
  58. firstDevGroup.add(b);
  59. firstDevGroup.add(c);
  60.  
  61. secondDevGroup.add(d);
  62. secondDevGroup.add(e);
  63. secondDevGroup.add(f);
  64. secondDevGroup.add(g);
  65.  
  66. // 再看销售部下的人员情况
  67. salesDep.add(h);
  68. salesDep.add(i);
  69.  
  70. // 最后一个财务
  71. financeDep.add(j);
  72.  
  73. // 树状结构写完毕,然后我们打印出来
  74. System.out.println(ceo.getInfo());
  75.  
  76. // 打印出来整个树形
  77. getAllSubordinateInfo(ceo.getSubordinateInfo());
  78. }
  79.  
  80. private static void getAllSubordinateInfo(ArrayList subordinateList){
  81. int length = subordinateList.size();
  82. for(int m=0;m<length;m++){
  83. Object s = subordinateList.get(m);
  84. if(s instanceof Leaf){
  85. // 是个叶子节点,也就是员工
  86. ILeaf employee = (ILeaf) s;
  87. System.out.println(employee.getInfo());
  88. }else{
  89. IBranch branch = (IBranch) s;
  90. System.out.println(branch.getInfo());
  91. // 在递归调用
  92. getAllSubordinateInfo(branch.getSubordinateInfo());
  93. }
  94. }
  95. }
  96. }

  这个程序比较长,如果是在我们的项目中有这样的程序,肯定是被拉出来做典型的,你写一大坨的程序给谁呀,以后还要维护的,程序是要短小精悍!幸运的是,我们是这为案例来讲解,而且就是指出这样组装这棵树是有问题,等会我们深入讲解。

  和我们期望要的结果一样,一棵完整的树就生成了,而且我们还能够遍历。看类图或程序的时候,你有没有发觉有问题?getInfo 每个接口都有为什么不能抽象出来?Root 类和Branch 类有什么差别?为什么要定义成两个接口两个类?如果我要加一个任职期限,你是不是每个类都需要修改?如果我要后序遍历(从员工找到他的上级领导)能做吗?——彻底晕菜了!

  问题很多,我们一个一个解决,先说抽象的问题,确实可以吧 IBranch 和 IRoot 合并成一个接口,这个我们先肯定下来,这是个比较大的改动,我们先画个类图:

  这个类图还是有点问题的,接口的作用是什么?定义共性,那 ILeaf 和 IBranch 是不是也有共性呢?有 getInfo(),我们是不是要把这个共性也已经封装起来呢?好,我们再修改一下类图:

  类图上有两个接口,ICorp 是公司所有人员的信息的接口类,不管你是经理还是员工,你都有名字,职位,薪水,这个定义成一个接口没有错,IBranch 有没有必要呢?我们先实现出来然后再说。

  先看 ICorp.java 源代码:

  1. package com.pattern.composite.advance;
  2.  
  3. /**
  4. * 公司类,定义每个员工都有信息
  5. * @author http://www.cnblogs.com/initial-road/
  6. *
  7. */
  8. public interface ICorp {
  9.  
  10. // 每个员工都有信息,你想隐藏,门儿都没有!
  11. public String getInfo();
  12.  
  13. }
  14.  
  15. // 接口很简单,只有一个方法,就是获得员工的信息,我们再来看实现类:
  16.  
  17. package com.pattern.composite.advance;
  18.  
  19. /**
  20. * Leaf是树枝节点,在这里就是我们这些小兵
  21. * @author http://www.cnblogs.com/initial-road/
  22. *
  23. */
  24. public class Leaf implements ICorp {
  25.  
  26. // 小兵也有名称
  27. private String name = "";
  28.  
  29. // 小兵也有职位
  30. private String position = "";
  31.  
  32. // 小兵也有薪水,否则谁给你干
  33. private int salary = 0;
  34.  
  35. // 通过一个构造函数传递小兵的信息
  36. public Leaf(String name, String position, int salary) {
  37. this.name = name;
  38. this.position = position;
  39. this.salary = salary;
  40. }
  41.  
  42. // 获得小兵的信息
  43. public String getInfo() {
  44. String info = "";
  45. info = "姓名:" + this.name;
  46. info = info + "\t职位:" + this.position;
  47. info = info + "\t薪水:" + this.salary;
  48. return info;
  49. }
  50.  
  51. }

  小兵就只有这些信息了,我们是具体干活的,我们是管理不了其他同事的,我们来看看那些经理和小组长是怎么实现的,先看接口:

  1. package com.pattern.composite.advance;
  2.  
  3. import java.util.ArrayList;
  4.  
  5. /**
  6. * 这些下边有小兵或者是经理等风云人物
  7. * @author http://www.cnblogs.com/initial-road/
  8. *
  9. */
  10. public interface IBranch {
  11.  
  12. // 能够增加小兵(树叶节点)或者是经过(树枝节点)
  13. public void addSubordinate(ICorp corp);
  14.  
  15. // 还要能够获得下属的信息
  16. public ArrayList<ICorp> getSubordinate();
  17.  
  18. /**
  19. * 本来还应该又一个方法 delSubordinate(ICorp corp),删除下属
  20. * 这个方法我们没有用到就不写进来了
  21. */
  22. }
  23.  
  24. // 接口也是很简单的,下面是实现类:
  25.  
  26. package com.pattern.composite.advance;
  27.  
  28. import java.util.ArrayList;
  29.  
  30. /**
  31. * 这些树枝节点也就是这些领导们既要自己的信息,还要知道自己的下属情况
  32. * @author http://www.cnblogs.com/initial-road/
  33. *
  34. */
  35. public class Branch implements IBranch, ICorp{
  36. // 领导也是人,也有名字
  37. private String name = "";
  38.  
  39. // 领导和领导不同,也是职位区别
  40. private String position = "";
  41.  
  42. // 领导也是拿薪水的
  43. private int salary = 0;
  44.  
  45. // 领导下边有那些下级领导和小兵
  46. ArrayList<ICorp> subordinateList = new ArrayList<ICorp>();
  47.  
  48. // 通过构造函数传递领导的信息
  49. public Branch(String name, String position, int salary) {
  50. this.name = name;
  51. this.position = position;
  52. this.salary = salary;
  53. }
  54.  
  55. // 增加一个下属,可能是小头目,也可能是个小兵
  56. public void addSubordinate(ICorp corp) {
  57. this.subordinateList.add(corp);
  58. }
  59.  
  60. // 我有哪些下属
  61. public ArrayList<ICorp> getSubordinate() {
  62. return this.subordinateList;
  63. }
  64.  
  65. // 领导也是人,他也有信息
  66. public String getInfo() {
  67. String info = "";
  68. info = "姓名:" + this.name;
  69. info = info + "\t职位:" + this.position;
  70. info = info + "\t薪水:" + this.salary;
  71. return info;
  72. }
  73. }
  74.  
  75. // 实现类也很简单,不多说,程序写的好不好,就看别人怎么调用了,我们看Client.java 程序:
  76.  
  77. package com.pattern.composite.advance;
  78.  
  79. import java.util.ArrayList;
  80.  
  81. /**
  82. * 组装这个树形结构,并展示出来
  83. * @author http://www.cnblogs.com/initial-road/
  84. *
  85. */
  86. @SuppressWarnings("all")
  87. public class Client {
  88.  
  89. public static void main(String[] args) {
  90.  
  91. // 首先是组装一个组织结构出来
  92. Branch ceo = compositeCorpTree();
  93.  
  94. // 首先把CEO的信息打印出来
  95. System.out.println(ceo.getInfo());
  96.  
  97. // 然后是所有员工信息
  98. System.out.println(getTreeInfo(ceo));
  99. }
  100.  
  101. // 把整个树组装出来
  102. public static Branch compositeCorpTree(){
  103. // 首先产生总经理CEO
  104. Branch root = new Branch("张三", "总经理", 10000);
  105.  
  106. // 把三个部门经理产生出来
  107. Branch developDep = new Branch("李四", "研发部门经理", 10000);
  108. Branch salesDep = new Branch("王五", "销售部门经理", 20000);
  109. Branch financeDep = new Branch("赵六", "财务部经理", 30000);
  110.  
  111. // 再把三个小组长产生出来
  112. Branch firstDevGroup = new Branch("周七", "开发一组组长", 5000);
  113. Branch secondDevGroup = new Branch("杨三", "开发二组组长", 6000);
  114.  
  115. // 把所有的小兵都产生出来
  116. Leaf a = new Leaf("a", "开发人员", 2000);
  117. Leaf b = new Leaf("b", "开发人员", 2000);
  118. Leaf c = new Leaf("c", "开发人员", 2000);
  119. Leaf d = new Leaf("d", "开发人员", 2000);
  120. Leaf e = new Leaf("e", "开发人员", 2000);
  121. Leaf f = new Leaf("f", "开发人员", 2000);
  122. Leaf g = new Leaf("g", "开发人员", 2000);
  123. Leaf h = new Leaf("h", "销售人员", 5000);
  124. Leaf i = new Leaf("i", "销售人员", 4000);
  125. Leaf j = new Leaf("j", "财务人员", 5000);
  126. Leaf k = new Leaf("k", "CEO秘书", 8000);
  127. Leaf zhengLaoLiu = new Leaf("郑老六", "研发部副经理", 20000);
  128.  
  129. // 开始组装
  130. // CEO下有三个部门经理和一个秘书
  131. root.addSubordinate(k);
  132. root.addSubordinate(developDep);
  133. root.addSubordinate(salesDep);
  134. root.addSubordinate(financeDep);
  135.  
  136. // 研发部经理
  137. developDep.addSubordinate(zhengLaoLiu);
  138. developDep.addSubordinate(firstDevGroup);
  139. developDep.addSubordinate(secondDevGroup);
  140.  
  141. // 看看开发两个小组下有什么
  142. firstDevGroup.addSubordinate(a);
  143. firstDevGroup.addSubordinate(b);
  144. firstDevGroup.addSubordinate(c);
  145. secondDevGroup.addSubordinate(d);
  146. secondDevGroup.addSubordinate(e);
  147. secondDevGroup.addSubordinate(f);
  148.  
  149. // 再看销售部下的人员情况
  150. salesDep.addSubordinate(h);
  151. salesDep.addSubordinate(i);
  152.  
  153. // 最后一个财务
  154. financeDep.addSubordinate(j);
  155.  
  156. return root;
  157. }
  158.  
  159. // 遍历整棵树,只要给我根节点,我就能遍历出所有的节点
  160. public static String getTreeInfo(Branch root){
  161. ArrayList<ICorp> subordinate = root.getSubordinate();
  162. String info = "";
  163. for(ICorp s : subordinate){
  164. // 是员工就直接获得信息
  165. if(s instanceof Leaf){
  166. info = info + s.getInfo() + "\n";
  167. }else{
  168. // 是个小头目
  169. info = info + s.getInfo() + "\n" + getTreeInfo((Branch) s);
  170. }
  171. }
  172. return info;
  173. }
  174.  
  175. }

  一个非常清晰的树状人员资源管理图出现了,那我们的程序是否还可以优化?可以! 你看 Leaf 和 Branch中都有 getInfo 信息,是否可以抽象,好,我们抽象一下:

  你一看这个图,乐了,能不乐嘛,减少很多工作量了,接口没有了,改成抽象类了,IBranch 接口也没有了,直接把方法放到了实现类中了,那我们先来看抽象类:

  1. package com.pattern.composite.perfect;
  2.  
  3. /**
  4. * 定义一个公司的人员的抽象类
  5. * @author http://www.cnblogs.com/initial-road/
  6. *
  7. */
  8. public abstract class Corp {
  9.  
  10. // 公司每个人都有名称
  11. private String name = "";
  12.  
  13. // 公司每个人都有职位
  14. private String position = "";
  15.  
  16. // 公司每个人都有薪水
  17. private int salary = 0;
  18.  
  19. /**
  20. * 通过接口的方式传递,我们改变一下习惯,传递进来的变量名以下划线开始
  21. * 这个在一些开源项目中非常常见,一般构造函数都是这么定义的
  22. */
  23. public Corp(String _name, String _position, int _salary){
  24. this.name = _name;
  25. this.position = _position;
  26. this.salary = _salary;
  27. }
  28.  
  29. // 获得员工信息
  30. public String getInfo(){
  31. String info = "";
  32. info = "姓名:" + this.name;
  33. info = info + "\t职位:" + this.position;
  34. info = info + "\t薪水:" + this.salary;
  35. return info;
  36. }
  37.  
  38. }
  39.  
  40. // 抽象类嘛,就应该抽象出一些共性的东西出来,然后看两个具体的实现类:
  41.  
  42. package com.pattern.composite.perfect;
  43.  
  44. /**
  45. * 普通员工很简单,就写一个函数就可以了
  46. * @author http://www.cnblogs.com/initial-road/
  47. *
  48. */
  49. public class Leaf extends Corp {
  50.  
  51. // 就写一个构造函数,这个是必须的
  52. public Leaf(String name, String position, int salary) {
  53. super(name, position, salary);
  54. }
  55.  
  56. }
  57.  
  58. // 这个改动比较多,就几行代码就完成了,确实就应该这样,下面是小头目的实现类:
  59.  
  60. package com.pattern.composite.perfect;
  61.  
  62. import java.util.ArrayList;
  63.  
  64. /**
  65. * 节点类,也简单了很多
  66. * @author http://www.cnblogs.com/initial-road/
  67. *
  68. */
  69. public class Branch extends Corp {
  70.  
  71. // 领导下边有那些下级领导和小兵
  72. ArrayList<Corp> subordinateList = new ArrayList<Corp>();
  73.  
  74. // 构造函数是必须的了
  75. public Branch(String _name, String _position, int _salary) {
  76. super(_name, _position, _salary);
  77. }
  78.  
  79. // 增加一个下属,可能是小头目,也可能是个小兵
  80. public void addSubordinate(Corp corp){
  81. this.subordinateList.add(corp);
  82. }
  83.  
  84. // 我有哪些下属
  85. public ArrayList<Corp> getSubordinate(){
  86. return this.subordinateList;
  87. }
  88.  
  89. }
  90.  
  91. // 也缩减了很多,再看 Client.java 程序,这个就没有多大变化了:
  92.  
  93. package com.pattern.composite.perfect;
  94.  
  95. import java.util.ArrayList;
  96.  
  97. /**
  98. * 组装这个树形结构,并展示出来
  99. * @author http://www.cnblogs.com/initial-road/
  100. *
  101. */
  102. public class Client {
  103.  
  104. public static void main(String[] args) {
  105.  
  106. // 首先是组装一个组织结构出来
  107. Branch ceo = compositeCorpTree();
  108.  
  109. // 首先把CEO的信息打印出来
  110. System.out.println(ceo.getInfo());
  111.  
  112. // 然后是所有员工信息
  113. System.out.println(getTreeInfo(ceo));
  114. }
  115.  
  116. // 把整个树组装出来
  117. public static Branch compositeCorpTree(){
  118. // 首先产生总经理CEO
  119. Branch root = new Branch("张三", "总经理", 10000);
  120.  
  121. // 把三个部门经理产生出来
  122. Branch developDep = new Branch("李四", "研发部门经理", 10000);
  123. Branch salesDep = new Branch("王五", "销售部门经理", 20000);
  124. Branch financeDep = new Branch("赵六", "财务部经理", 30000);
  125.  
  126. // 再把三个小组产生出来
  127. Branch firstDevGroup = new Branch("杨三", "开发一组组长", 5000);
  128. Branch secondDevGroup = new Branch("吴大", "开发二组组长", 6000);
  129.  
  130. // 把所有的小兵都产生出来
  131. Leaf a = new Leaf("a", "开发人员", 2000);
  132. Leaf b = new Leaf("b", "开发人员", 2000);
  133. Leaf c = new Leaf("c", "开发人员", 2000);
  134. Leaf d = new Leaf("d", "开发人员", 2000);
  135. Leaf e = new Leaf("e", "开发人员", 2000);
  136. Leaf f = new Leaf("f", "开发人员", 2000);
  137. Leaf g = new Leaf("g", "开发人员", 2000);
  138. Leaf h = new Leaf("h", "销售人员", 5000);
  139. Leaf i = new Leaf("i", "销售人员", 4000);
  140. Leaf j = new Leaf("j", "财务人员", 5000);
  141. Leaf k = new Leaf("k", "CEO秘书", 8000);
  142. Leaf zhengLaoLiu = new Leaf("郑老六", "研发部副总理", 20000);
  143.  
  144. // 开始组装
  145. // CEO下有三个部门经理和一个秘书
  146. root.addSubordinate(k);
  147. root.addSubordinate(developDep);
  148. root.addSubordinate(salesDep);
  149. root.addSubordinate(financeDep);
  150.  
  151. // 研发部门经理
  152. developDep.addSubordinate(zhengLaoLiu);
  153. developDep.addSubordinate(firstDevGroup);
  154. developDep.addSubordinate(secondDevGroup);
  155.  
  156. // 看看两个开发小组下有什么
  157. firstDevGroup.addSubordinate(a);
  158. firstDevGroup.addSubordinate(b);
  159. firstDevGroup.addSubordinate(c);
  160. secondDevGroup.addSubordinate(d);
  161. secondDevGroup.addSubordinate(e);
  162. secondDevGroup.addSubordinate(f);
  163. secondDevGroup.addSubordinate(g);
  164.  
  165. // 再看销售部下的人员情况
  166. salesDep.addSubordinate(h);
  167. salesDep.addSubordinate(i);
  168.  
  169. // 最后一个财务
  170. financeDep.addSubordinate(j);
  171. return root;
  172. }
  173.  
  174. // 遍历整棵树,只要给我根节点,我就能遍历出所有的节点
  175. public static String getTreeInfo(Branch branch){
  176. ArrayList<Corp> subordinateList = branch.getSubordinate();
  177. String info = "";
  178. for(Corp s : subordinateList){
  179. if(s instanceof Leaf){
  180. // 是员工就直接获得信息
  181. info = info + s.getInfo() + "\n";
  182. }else{
  183. info = info + s.getInfo() + "\n" + getTreeInfo((Branch) s);
  184. }
  185. }
  186. return info;
  187. }
  188. }

  就是把用到 ICorp 接口的地方修改为 Corp 抽象类就成了,就上面黄色的部分作了点修改,其他保持不变。

  确实是类、接口减少了很多,而且程序也简单很多,但是大家可能还是很迷茫,这个 Client 程序并没有改变多少呀,非常正确,树的组装你是跑不了的,你要知道在项目中使用数据库来存储这些信息的,你从数据库中提出来哪些人要分配到树枝,哪些人要分配到树叶,树枝与树枝、树叶的关系,这些都需要人去定义,通常这里使用一个界面去配置,在数据库中是一个标志信息,例如定义这样一张表:

  从这张表中已经定义个一个树形结构,我们要做的就是从数据库中读取出来,然后展现到前台上,这个读取就用个 for 循环加上递归是不是就可以把一棵树建立起来?我们程序中其实还包涵了数据的读取和加工,用了数据库后,数据和逻辑已经在表中定义好了,我们直接读取放到树上就可以了,这个还是比较容易做了的,大家不妨自己考虑一下。

  上面我们讲到的就是组合模式(也叫合成模式),有时又叫做部分-整体模式(Part-Whole),主要是用来描述整体与部分的关系,用的最多的地方就是树形结构。组合模式通用类图如下:

  我们先来说说组合模式的几个角色:

  抽象构件角色(Component): 定义参加组合的对象的共有方法和属性,可以定义一些默认的行为或属性;比如我们例子中的 getInfo 就封装到了抽象类中。

   叶子构件(Leaf):叶子对象,其下再也没有其他的分支。

  树枝构件(Composite):树枝对象,它的作用是组合树枝节点和叶子节点;

  组合模式有两种模式,透明模式和安全模式,这两个模式有什么区别呢?先看类图:

  从类图上大家应该能看清楚了,这两种模式各有优缺点,透明模式是把用来组合使用的方法放到抽象类中, 比如 add(),remove()以及 getChildren等方法(顺便说一下, getChildren 一般返回的结果为 Iterable的实现类,很多,大家可以看 JDK 的帮助),不管叶子对象还是树枝对象都有相同的结构,通过判断是getChildren 的返回值确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题的,不是很建议的方式;安全模式就不同了,它是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全,我们的例子使用了安全模式。

  组合模式的优点有哪些呢?第一个优点只要是树形结构,就要考虑使用组合模式,这个一定记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式吧。组合模式有一个非常明显的缺点,看到我们在 Client.java 中的的定义了树叶和树枝使用时的定义了吗?如下:

  发现什么问题了吗?直接使用了实现类!这个在面向接口编程上是很不恰当的,这个在使用的时候要考虑清楚。

  组合模式在项目中到处都有,比如现在的页面结构一般都是上下结构,上面放系统的 Logo,下边分为两部分:左边是导航菜单,右边是展示区,左边的导航菜单一般都是树形的结构,比较清晰, 这个 JavaScript有很多例子,大家可以到网上搜索一把;还有,我们的自己也是一个树状结构,根据我,能够找到我的父母,根据父亲又能找到爷爷奶奶,根据母亲能够找到外公外婆等等,很典型的树形结构,而且还很规范(这个要是不规范那肯定是乱套了)。

  我们在上面也还提到了一个问题,就是树的遍历问题,从上到下遍历没有问题,但是我要是从下往上遍历呢?比如在人力资源这颗树上,我从中抽取一个用户,要找到它的上级有哪些,下级有哪些,怎么处理?想想,~~~,再想想!想出来了吧,我们对下答案,先看类图:

  看类图中的红色方框,只要增加两个方法就可以了,一个是设置父节点是谁,一个是查找父节点是谁,我们来看一下程序的改变:

  1. package com.pattern.composite.perfect;
  2.  
  3. /**
  4. * 定义一个公司的人员的抽象类
  5. * @author http://www.cnblogs.com/initial-road/
  6. *
  7. */
  8. public abstract class Corp {
  9.  
  10. // 公司每个人都有名称
  11. private String name = "";
  12.  
  13. // 公司每个人都有职位
  14. private String position = "";
  15.  
  16. // 公司每个人都有薪水
  17. private int salary = 0;
  18.  
  19. // 父节点是谁
  20. protected Corp pattern = null;
  21.  
  22. /**
  23. * 通过接口的方式传递,我们改变一下习惯,传递进来的变量名以下划线开始
  24. * 这个在一些开源项目中非常常见,一般构造函数都是这么定义的
  25. */
  26. public Corp(String _name, String _position, int _salary){
  27. this.name = _name;
  28. this.position = _position;
  29. this.salary = _salary;
  30. }
  31.  
  32. // 获得员工信息
  33. public String getInfo(){
  34. String info = "";
  35. info = "姓名:" + this.name;
  36. info = info + "\t职位:" + this.position;
  37. info = info + "\t薪水:" + this.salary;
  38. return info;
  39. }
  40.  
  41. // 得到父节点
  42. public Corp getPattern() {
  43. return pattern;
  44. }
  45.  
  46. // 设置父节点
  47. public void setPattern(Corp pattern) {
  48. this.pattern = pattern;
  49. }
  50.  
  51. }
  52.  
  53. // 就增加了父节点部分,然后我们再来看看 Branch.java 的改变:
  54.  
  55. package com.pattern.composite.perfect;
  56.  
  57. import java.util.ArrayList;
  58.  
  59. /**
  60. * 节点类,也简单了很多
  61. * @author http://www.cnblogs.com/initial-road/
  62. *
  63. */
  64. public class Branch extends Corp {
  65.  
  66. // 领导下边有那些下级领导和小兵
  67. ArrayList<Corp> subordinateList = new ArrayList<Corp>();
  68.  
  69. // 构造函数是必须的了
  70. public Branch(String _name, String _position, int _salary) {
  71. super(_name, _position, _salary);
  72. }
  73.  
  74. // 增加一个下属,可能是小头目,也可能是个小兵
  75. public void addSubordinate(Corp corp){
  76. // 设置父节点
  77. corp.setPattern(this);
  78. this.subordinateList.add(corp);
  79. }
  80.  
  81. // 我有哪些下属
  82. public ArrayList<Corp> getSubordinate(){
  83. return this.subordinateList;
  84. }
  85.  
  86. }

  增加了父节点部分,看懂程序了吗?就是在每个节点甭管是树枝节点还是树叶节点,都增加了一个属性:父节点对象,这样在树枝节点增加子节点或叶子的时候设置父节点,然后你看整棵树就除了根节点外每个节点都一个父节点,剩下的事情还不好处理吗?每个节点上都有父节点了,你要往上找,那就找呗!Client程序我就不写了,今天已经拷贝的代码实在有点多,大家自己考虑一下,写个 find 方法,然后一个一个往上找,最简单的方法了!

  有了这个 parent 属性,什么后序遍历(从下往上找)、中序遍历(从中间某个环节往上或往下遍历)都解决了,这个就不多说了。

  再提一个问题,树叶节点和树枝节点是有顺序的,你不能乱排的,怎么办?比如我们上面的例子,研发一组下边有三个成员,这三个成员是要进行排序的呀,你怎么处理?问我呀,问你呢,好好想想,以后用到着的!

24种设计模式--组合模式【Composite Pattern】的更多相关文章

  1. 浅谈设计模式--组合模式(Composite Pattern)

    组合模式(Composite Pattern) 组合模式,有时候又叫部分-整体结构(part-whole hierarchy),使得用户对单个对象和对一组对象的使用具有一致性.简单来说,就是可以像使用 ...

  2. 设计模式 - 组合模式(composite pattern) 迭代器(iterator) 具体解释

    组合模式(composite pattern) 迭代器(iterator) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考组合模式(composit ...

  3. C#设计模式——组合模式(Composite Pattern)

    一.概述 在软件开发中,我们往往会遇上类似树形结构的对象体系.即某一对象既可能在树形结构中作为叶节点存在,也可能作为分支节点存在.比如在文件系统中,文件是作为叶节点存在,而文件夹就是分支节点.在设计这 ...

  4. 设计模式 -- 组合模式 (Composite Pattern)

    定义: 对象组合成部分整体结构,单个对象和组合对象具有一致性. 看了下大概结构就是集团总公司和子公司那种层级结构. 角色介绍: Component :抽象根节点:其实相当去总公司,抽象子类共有的方法: ...

  5. 24种设计模式-策略模式(Strategy Pattern)

    一.优点: 1. 策略模式提供了管理相关的算法族的办法.策略类的等级结构定义了一个算法或行为族.恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码. 2. 策略模式提供了可以替换继承关系的 ...

  6. 二十四种设计模式:组合模式(Composite Pattern)

    组合模式(Composite Pattern) 介绍将对象组合成树形结构以表示"部分-整体"的层次结构.它使得客户对单个对象和复合对象的使用具有一致性.示例有一个Message实体 ...

  7. 设计模式系列之组合模式(Composite Pattern)——树形结构的处理

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  8. 乐在其中设计模式(C#) - 组合模式(Composite Pattern)

    原文:乐在其中设计模式(C#) - 组合模式(Composite Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 组合模式(Composite Pattern) 作者:weba ...

  9. 【设计模式】组合模式 Composite Pattern

    树形结构是软件行业很常见的一种结构,几乎随处可见,  比如: HTML 页面中的DOM,产品的分类,通常一些应用或网站的菜单,Windows Form 中的控件继承关系,Android中的View继承 ...

随机推荐

  1. html5 做游戏 Quintus Sublime Text牛逼的神器

  2. 转储指定的数据块并查看TRC信息

    1.转储指定的块:需要两个信息:文件号和块号 BYS@bys1>alter system dump datafile 1 block 100; System altered. 2.定位找出use ...

  3. 跑马灯效果的TextView之singLine 和maxLines

    Android 的TextView 里面有两个属性 singLine 和maxLines . 从字面意思来理解,这两个都是限制Text的行数.那么singleLine="true" ...

  4. 如何用C#检查硬盘是否是固态硬盘SSD

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:如何用C#检查硬盘是否是固态硬盘SSD.

  5. maven中使用net.sf.json-lib

    <dependency> <groupId>net.sf.json-lib</groupId> <artifactId>json-lib</art ...

  6. JSON.NET概述

    1. JSON.NET概述 当JSON逐渐成为Ajax的标准数据交互格式时,在.NET中处理JSON数据只能使用字符串拼接的方法,十分麻烦,因而催生了JSON.NET这个项目. JSON.NET是一个 ...

  7. Delphi 第三方组件

    TMS Component Pack v7.0.0.0 TMS Component Pack 版本为Delphi和C++ Builder提供了超过350个VCL组件,用以创建功能丰富的.现代的和原生W ...

  8. careercup-排序和查找 11.4

    11.4 设想你有一个20GB的文件,每一行一个字符串.请说明将如何对这个文件进行排序. 解法: 当面试官给出20GB大小的限制时,实际上在暗示些什么.就此题而言,这表明他们不希望你将数据全部载入内存 ...

  9. linux gnome 安装

    首先先下载x-window的内核:apt-get -u install x-window-system-core:下载登录管理界面gdm或kdm:apt-get -u install gdm gdm- ...

  10. MVC1笔记

    /// ///直接返回 字符串的 Action方法,适用于 不需要返回大量 html代码的业务(类似于一般处理程序) /// public string Index() { return " ...