大话设计模式笔记(十六)の组合模式

组合模式

定义

将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

UML图

模板代码

Component

 1.  /**
 2.   * 组合对象
 3.   * Created by callmeDevil on 2019/8/11.
 4.   */
 5.  public abstract class Component {
 6.  
 7.      protected String name;
 8.  
 9.      public Component(String name){
10.          this.name = name;
11.      }
12.  
13.      // 通常都用add 和remove 方法来提供增加或移除树叶或树枝的功能
14.      public abstract void add(Component c);
15.      public abstract void remove(Component c);
16.      public abstract void dispaly(int depth);
17.  
18.  }
19.  

Composite

 1.  /**
 2.   * 枝节点行为,用来存储子部件
 3.   * Created by callmeDevil on 2019/8/11.
 4.   */
 5.  public class Composite extends Component{
 6.  
 7.      // 一个子对象集合用来存储其下属枝节点和叶节点
 8.      private List<Component> children = new ArrayList<>();
 9.  
10.      public Composite(String name){
11.          super(name);
12.      }
13.  
14.      @Override
15.      public void add(Component c) {
16.          children.add(c);
17.      }
18.  
19.      @Override
20.      public void remove(Component c) {
21.          children.remove(c);
22.      }
23.  
24.      @Override
25.      public void dispaly(int depth) {
26.          StringBuilder sb = new StringBuilder(depth);
27.          for (int i = 0; i < depth; i++) {
28.              sb.append("-");
29.          }
30.          // 显示枝节点名称,并对其下级进行遍历
31.          System.out.println(String.format("%s %s", sb.toString(), name));
32.          for (Component child : children) {
33.              child.dispaly(depth + 3);
34.          }
35.      }
36.  
37.  }
38.  

Leaf

 1.  /**
 2.   * 叶节点对象
 3.   * Created by callmeDevil on 2019/8/11.
 4.   */
 5.  public class Leaf extends Component {
 6.  
 7.      public Leaf(String name) {
 8.          super(name);
 9.      }
10.  
11.      @Override
12.      public void add(Component c) {
13.          System.out.println("cannot add to a leaf");
14.      }
15.  
16.      @Override
17.      public void remove(Component c) {
18.          System.out.println("cannot remove from a leaf");
19.      }
20.  
21.      @Override
22.      public void dispaly(int depth) {
23.          StringBuilder sb = new StringBuilder(depth);
24.          for (int i = 0; i < depth; i++) {
25.              sb.append("-");
26.          }
27.          // 显示名称和级别
28.          System.out.println(String.format("%s %s", sb.toString(), name));
29.      }
30.  
31.  }
32.  

测试

 1.  public class Test {
 2.      public static void main(String[] args) {
 3.          // 生成树根root,根上长出两叶LeafA 和LeafB
 4.          Composite root = new Composite("root");
 5.          root.add(new Leaf("Leaf A"));
 6.          root.add(new Leaf("Leaf B"));
 7.          // 根上长出分枝 Composite X ,分枝上也有两叶LeafXA 和LeafXB
 8.          Composite comp = new Composite("Composite X");
 9.          comp.add(new Leaf("Leaf XA"));
10.          comp.add(new Leaf("Leaf XB"));
11.          root.add(comp);
12.          // 分枝 Composite X上再长出分枝 Composite XY ,分枝上也有两叶Leaf XYA 和Leaf XYB
13.          Composite comp2 = new Composite("Composite XY");
14.          comp2.add(new Leaf("Leaf XYA"));
15.          comp2.add(new Leaf("Leaf XYB"));
16.          comp.add(comp2);
17.          // 根部又长出两叶LeafC 和LeafD,可惜LeafD没长牢,被风吹走了
18.          root.add(new Leaf("Leaf C"));
19.          Leaf leafD = new Leaf("Leaf D");
20.          root.add(leafD);
21.          root.remove(leafD);
22.          // 显示大树
23.          root.dispaly(1);
24.      }
25.  }
26.  

测试结果

 1.  - root
 2.  ---- Leaf A
 3.  ---- Leaf B
 4.  ---- Composite X
 5.  ------- Leaf XA
 6.  ------- Leaf XB
 7.  ------- Composite XY
 8.  ---------- Leaf XYA
 9.  ---------- Leaf XYB
10.  ---- Leaf C
11.  

透明方式与安全方式

树可能有无数的分枝,反复使用 Composite 就可以实现树状结构,但树叶不可以再长分枝,为什么此处 Leaf 当中也有实现 add 和 remove?

这种叫做透明方式,也就是说在 Component 中声明所有用来管理子对象的方法,其中包括 add, remove 等。这样实现 Component 接口的所有子类都具备了 add 和 remove。这样做的好处是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口,但问题也明显,因为本身不具体 add 和 remove 功能,所以实现是没有意义的。

当然也可以不去实现,这种叫做安全方式,也就是在 Component 接口中不去声明 add 和 remove 方法,那么子类的 Leaf 也就不需要去实现,而是在 Composite 声明所有用来管理子类对象的方法,不过由于不够透明,所以树叶和树枝将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。

何时使用?

当需求中是体现部分与整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象时,就应该考虑用组合模式了。

举个栗子

问题描述

总公司与分公司关系,总公司的所有管理功能同样能使用在分公司。

代码实现

Company

 1.  /**
 2.   * 公司抽象类
 3.   * Created by callmeDevil on 2019/8/11.
 4.   */
 5.  public abstract class Company {
 6.  
 7.      protected String name;
 8.  
 9.      public Company(String name){
10.          this.name = name;
11.      }
12.  
13.      public abstract void add(Company c);
14.      public abstract void remove(Company c);
15.      public abstract void dispaly(int depth);
16.      public abstract void lineOfDuty(); // 履行职责,不同部门需要履行不同的职责
17.  
18.      // 用于输出层次结构,非必须,与模式无关
19.      public String getDepth(int depth){
20.          StringBuilder sb = new StringBuilder(depth);
21.          for (int i = 0; i < depth; i++) {
22.              sb.append("-");
23.          }
24.          return sb.toString();
25.      }
26.  
27.  }
28.  

ConcreteCompany

 1.  /**
 2.   * 具体公司(树枝节点)
 3.   * Created by callmeDevil on 2019/8/11.
 4.   */
 5.  public class ConcreteCompany extends Company {
 6.  
 7.      private List<Company> children = new ArrayList<>();
 8.  
 9.      public ConcreteCompany(String name) {
10.          super(name);
11.      }
12.  
13.      @Override
14.      public void add(Company c) {
15.          children.add(c);
16.      }
17.  
18.      @Override
19.      public void remove(Company c) {
20.          children.remove(c);
21.      }
22.  
23.      @Override
24.      public void dispaly(int depth) {
25.          System.out.println(String.format("%s %s", getDepth(depth), name));
26.          for (Company child : children) {
27.              child.dispaly(depth + 3);
28.          }
29.      }
30.  
31.      @Override
32.      public void lineOfDuty() {
33.          for (Company child : children) {
34.              child.lineOfDuty();
35.          }
36.      }
37.  
38.  }
39.  

HRDepartment

 1.  /**
 2.   * 人力资源部(树叶节点)
 3.   * Created by callmeDevil on 2019/8/11.
 4.   */
 5.  public class HRDepartment extends Company{
 6.  
 7.      public HRDepartment(String name){
 8.          super(name);
 9.      }
10.  
11.      @Override
12.      public void add(Company c) {}
13.  
14.      @Override
15.      public void remove(Company c) {}
16.  
17.      @Override
18.      public void dispaly(int depth) {
19.          System.out.println(String.format("%s %s", getDepth(depth), name));
20.      }
21.  
22.      @Override
23.      public void lineOfDuty() {
24.          System.out.println(String.format("%s 员工招聘培训管理", name));
25.      }
26.  
27.  }
28.  

FinanceDepartment

 1.  /**
 2.   * 财务部(树叶节点)
 3.   * Created by callmeDevil on 2019/8/11.
 4.   */
 5.  public class FinanceDepartment extends Company{
 6.  
 7.      public FinanceDepartment(String name){
 8.          super(name);
 9.      }
10.  
11.      @Override
12.      public void add(Company c) {}
13.  
14.      @Override
15.      public void remove(Company c) {}
16.  
17.      @Override
18.      public void dispaly(int depth) {
19.          System.out.println(String.format("%s %s", getDepth(depth), name));
20.      }
21.  
22.      @Override
23.      public void lineOfDuty() {
24.          System.out.println(String.format("%s 公司财务收支管理", name));
25.      }
26.  
27.  }
28.  

测试

 1.  public class Test {
 2.      public static void main(String[] args) {
 3.          ConcreteCompany root = new ConcreteCompany("北京总公司");
 4.          root.add(new HRDepartment("总公司人力资源部"));
 5.          root.add(new FinanceDepartment("总公司财务部"));
 6.  
 7.          ConcreteCompany comp = new ConcreteCompany("上海分公司");
 8.          comp.add(new HRDepartment("上海分公司人力资源部"));
 9.          comp.add(new FinanceDepartment("上海分公司财务部"));
10.          root.add(comp);
11.  
12.          ConcreteCompany comp1 = new ConcreteCompany("南京办事处");
13.          comp1.add(new HRDepartment("南京办事处人力资源部"));
14.          comp1.add(new FinanceDepartment("南京办事处财务部"));
15.          comp.add(comp1);
16.  
17.          ConcreteCompany comp2 = new ConcreteCompany("杭州办事处");
18.          comp2.add(new HRDepartment("杭州办事处人力资源部"));
19.          comp2.add(new FinanceDepartment("杭州办事处财务部"));
20.          comp.add(comp2);
21.  
22.          System.out.println("\n 结构图:");
23.          root.dispaly(1);
24.  
25.          System.out.println("\n 职责:");
26.          root.lineOfDuty();
27.      }
28.  }
29.  

测试结果

 1.   结构图:
 2.  - 北京总公司
 3.  ---- 总公司人力资源部
 4.  ---- 总公司财务部
 5.  ---- 上海分公司
 6.  ------- 上海分公司人力资源部
 7.  ------- 上海分公司财务部
 8.  ------- 南京办事处
 9.  ---------- 南京办事处人力资源部
10.  ---------- 南京办事处财务部
11.  ------- 杭州办事处
12.  ---------- 杭州办事处人力资源部
13.  ---------- 杭州办事处财务部
14.  
15.   职责:
16.  总公司人力资源部 员工招聘培训管理
17.  总公司财务部 公司财务收支管理
18.  上海分公司人力资源部 员工招聘培训管理
19.  上海分公司财务部 公司财务收支管理
20.  南京办事处人力资源部 员工招聘培训管理
21.  南京办事处财务部 公司财务收支管理
22.  杭州办事处人力资源部 员工招聘培训管理
23.  杭州办事处财务部 公司财务收支管理
24.  

总结

  • 组合模式定义了包含基本对象、组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
  • 用户是不用关心到底是处理一个叶节点对象还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。
  • 组合模式让客户可以一致的使用组合结构和单个对象。
posted @   callmeDevil  阅读(427)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示