设计模式之组合模式

袭人
• 阅读 775

一、什么是组合模式

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

  所以当我们的案例是树形结构或者是部分-整体的关系时,就可以考虑使用组合模式。

  组合模式有两种不同的实现,分别为透明模式和安全模式,下面将详细说明一下两种实现的区别。

  先说明一下UML图中各角色的职责。Component是对象声明接口,在适当情况下,实现所有类共有接口的默认行为;Leaf是叶子节点对象,其没有子节点;Composite是树枝节点对象,用来存储部件,组合树枝节点和叶子节点形成一个树形结构。

  下面这两种方式我们共用同一套客户端,先将客户端代码放上。

1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         //创建根节点及其子节点
 5         Composite root = new Composite("root");
 6         root.add(new Leaf("Leaf A"));
 7         root.add(new Leaf("Leaf B"));
 8 
 9         //创建第二层节点及其子节点
10         Composite branch = new Composite("Composite X");
11         branch.add(new Leaf("Leaf XA"));
12         branch.add(new Leaf("Leaf XB"));
13         root.add(branch);
14         
15         //创建第三层节点及其子节点
16         Composite branch2 = new Composite("Composite XY");
17         branch2.add(new Leaf("Leaf XYA"));
18         branch2.add(new Leaf("Leaf XYB"));
19         branch.add(branch2);
20         
21         //创建第二层节点
22         root.add(new Leaf("Leaf C"));
23         
24         //创建第二层节点并删除
25         Leaf leaf = new Leaf("Leaf D");
26         root.add(leaf);
27         root.remove(leaf);
28         
29         //打印
30         root.display(1);
31     }
32     
33 }

二、组合模式之透明模式

  透明模式是把组合使用的方法放到抽象类中,不管叶子对象还是树枝对象都有相同的结构,这样做的好处就是叶子节点和树枝节点对于外界没有区别,它们具备完全一致的行为接口。但因为Leaf类本身不具备add()、remove()方法的功能,所以实现它是没有意义的。UML结构图如下:

设计模式之组合模式

  1. Component

1 public abstract class Component {
 2     
 3     protected String name;
 4     
 5     public Component(String name) {
 6         this.name = name;
 7     }
 8 
 9     //增加一个叶子构件或树枝构件
10     public abstract void add(Component component);
11     
12     //删除一个叶子构件或树枝构件
13     public abstract void remove(Component component);
14     
15     //获取分支下的所有叶子构件和树枝构件
16     public abstract void display(int depth);
17     
18 }

  2. Composite

1 public class Composite extends Component {
 2 
 3     public Composite(String name) {
 4         super(name);
 5     }
 6 
 7     //构建容器
 8     private ArrayList<Component> componentArrayList = new ArrayList<Component>();
 9     
10     @Override
11     public void add(Component component) {
12         this.componentArrayList.add(component);
13     }
14 
15     @Override
16     public void remove(Component component) {
17         this.componentArrayList.remove(component);
18     }
19 
20     @Override
21     public void display(int depth) {
22         //输出树形结构
23         for(int i=0; i<depth; i++) {
24             System.out.print('-');
25         }
26         System.out.println(name);
27         
28         //下级遍历
29         for (Component component : componentArrayList) {
30             component.display(depth + 1);
31         }
32     }
33 
34 }

  3. Leaf

1 public class Leaf extends Component {
 2 
 3     public Leaf(String name) {
 4         super(name);
 5     }
 6 
 7     @Override
 8     public void add(Component component) {
 9         //空实现,抛出“不支持请求”异常
10         throw new UnsupportedOperationException();
11     }
12 
13     @Override
14     public void remove(Component component) {
15         //空实现,抛出“不支持请求”异常
16         throw new UnsupportedOperationException();
17     }
18 
19     @Override
20     public void display(int depth) {
21         //输出树形结构的叶子节点
22         for(int i=0; i<depth; i++) {
23             System.out.print('-');
24         }
25         System.out.println(name);
26     }
27 
28 }

  通过组合模式输出一个树形结构,运行结果如下:

设计模式之组合模式

三、组合模式之安全模式

  安全模式是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全。但由于不够透明,所以树叶节点和树枝节点将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。UML结构图如下:

设计模式之组合模式

  1. Component

  这里相比透明模式就少了add()和romove()抽象方法的声明。

1 public abstract class Component {
 2     
 3     protected String name;
 4     
 5     public Component(String name) {
 6         this.name = name;
 7     }
 8 
 9     //获取分支下的所有叶子构件和树枝构件
10     public abstract void display(int depth);
11     
12 }

  2. Composite

  这里add()和remove()方法的实现就从继承变为了自己实现。

1 public class Composite extends Component {
 2 
 3     public Composite(String name) {
 4         super(name);
 5     }
 6 
 7     //构建容器
 8     private ArrayList<Component> componentArrayList = new ArrayList<Component>();
 9     
10     //增加一个叶子构件或树枝构件
11     public void add(Component component) {
12         this.componentArrayList.add(component);
13     }
14 
15     //删除一个叶子构件或树枝构件
16     public void remove(Component component) {
17         this.componentArrayList.remove(component);
18     }
19 
20     @Override
21     public void display(int depth) {
22         //输出树形结构
23         for(int i=0; i<depth; i++) {
24             System.out.print('-');
25         }
26         System.out.println(name);
27         
28         //下级遍历
29         for (Component component : componentArrayList) {
30             component.display(depth + 1);
31         }
32     }
33 
34 }

  3. Leaf

  叶子节点中没有了空实现,比较安全。

1 public class Leaf extends Component {
 2 
 3     public Leaf(String name) {
 4         super(name);
 5     }
 6 
 7     @Override
 8     public void display(int depth) {
 9         //输出树形结构的叶子节点
10         for(int i=0; i<depth; i++) {
11             System.out.print('-');
12         }
13         System.out.println(name);
14     }
15 
16 }

  运行结果如下:

设计模式之组合模式

  由此可看出两个方法是相同的运行结果,区别在于内部实现不同,一种是叶节点与树枝节点具备一致的行为接口但有空实现的透明模式,另一种是树枝节点单独拥有用来组合的方法但调用不便的安全模式。

  为什么说它调用不便呢,因为我们如果通过递归遍历树时,这时需要判断当前节点是叶子节点还是树枝节点,客户端就需要相应的判断。

四、组合模式的应用

  1. 何时使用

  • 想表达“部分-整体”层次结构(树形结构)时
  • 希望用户忽略组合对象与单个对象的不同,用户将统一的使用组合结构中的所有对象

  2. 方法

  • 树枝和叶子实现统一接口,树枝内部组合该接口

  3. 优点

  • 高层模块调用简单。一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,高层模块不必关心自己处理的是单个对象还是整个组合结构。
  • 节点自由增加

  4. 缺点

  • 使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒转原则

  5. 使用场景

  • 维护和展示部分-整体关系的场景(如树形菜单、文件和文件夹管理)
  • 从一个整体中能够独立出部分模块或功能的场景

五、组合模式的实现

  下面我们以公司的层级结构为例,先看一下这个例子中该公司的层级结构(该例选自大话设计模式——程杰著)。

设计模式之组合模式

  这种部分与整体的关系,我们就可以考虑使用组合模式,下面采用组合模式的透明模式对其实现,UML图如下:

设计模式之组合模式

  1. 具体公司类

  此为树枝节点,实现添加、移除、显示和履行职责四种方法。

1 public class ConcreteCompany extends Company {
 2 
 3     private List<Company> companyList = new ArrayList<Company>();
 4     
 5     public ConcreteCompany(String name) {
 6         super(name);
 7     }
 8 
 9     @Override
10     public void add(Company company) {
11         this.companyList.add(company);
12     }
13 
14     @Override
15     public void remove(Company company) {
16         this.companyList.remove(company);
17     }
18 
19     @Override
20     public void display(int depth) {
21         //输出树形结构
22         for(int i=0; i<depth; i++) {
23             System.out.print('-');
24         }
25         System.out.println(name);
26         
27         //下级遍历
28         for (Company component : companyList) {
29             component.display(depth + 1);
30         }
31     }
32 
33     @Override
34     public void lineOfDuty() {
35         //职责遍历
36         for (Company company : companyList) {
37             company.lineOfDuty();
38         }
39     }
40 
41 }

  2. 人力资源部

  叶子节点,add和remove方法空实现。

1 public class HRDepartment extends Company {
 2 
 3     public HRDepartment(String name) {
 4         super(name);
 5     }
 6 
 7     @Override
 8     public void add(Company company) {
 9         
10     }
11 
12     @Override
13     public void remove(Company company) {
14         
15     }
16 
17     @Override
18     public void display(int depth) {
19         //输出树形结构的子节点
20         for(int i=0; i<depth; i++) {
21             System.out.print('-');
22         }
23         System.out.println(name);
24     }
25 
26     @Override
27     public void lineOfDuty() {
28         System.out.println(name + " : 员工招聘培训管理");
29     }
30     
31 }

  3. 财务部

  叶子节点,add和remove方法空实现。

1 public class FinanceDepartment extends Company {
 2     
 3     public FinanceDepartment(String name) {
 4         super(name);
 5     }
 6 
 7     @Override
 8     public void add(Company company) {
 9         
10     }
11 
12     @Override
13     public void remove(Company company) {
14         
15     }
16 
17     @Override
18     public void display(int depth) {
19         //输出树形结构的子节点
20         for(int i=0; i<depth; i++) {
21             System.out.print('-');
22         }
23         System.out.println(name);
24     }
25 
26     @Override
27     public void lineOfDuty() {
28         System.out.println(name + " : 公司财务收支管理");
29     }
30     
31 }

  4. Client客户端

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

  运行结果如下:

设计模式之组合模式

  组合模式这样就定义了包含人力资源部和财务部这些基本对象和分公司、办事处等组合对象的类层次结构。

  基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。

  这里用了透明模式,用户不用关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。简单点说就是组合模式可以让客户一致地使用组合结构和单个对象。

  

关注公众号:java宝典
设计模式之组合模式
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
4年前
java中的23种设计模式
java中一共23种设计模式!按照目的来分,设计模式可以分为创建型模式、结构型模式和行为型模式。创建型模式用来处理对象的创建过程;结构型模式用来处理类或者对象的组合;行为型模式用来对类或对象怎样交互和怎样分配职责进行描述。创建型模式用来处理对象的创建过程,主要包含以下5种设计模式:工厂方法模
Wesley13 Wesley13
4年前
Java计模模式之六
前言在上一篇(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fzhaosq%2Fp%2F10219533.html)中我们学习了结构型模式的外观模式和装饰器模式。本篇则来学习下组合模式和过滤器模式。组合模式简介
Stella981 Stella981
4年前
FusionCharts创建气泡图和散点图(二)
混合模式此模式是自动模式和类别模式的组合。它允许x轴同时显示自动计算的x轴标签以及显式定义的x轴标签。具有在混合模式下呈现的x轴标签的气泡图如下所示:!(https://image.evget.com/attachment/keditor/image/20200825/20200825104040_56090.png)在上图中,您
Stella981 Stella981
4年前
Android小白的探索:2D绘图之Android简易版Microsoft Visio学习之路 二、 自定义xml生成与解析
  今天天分享下如何通过组合模式,在sd卡中,写一个xml文件来保存这个组合类,及如何读取,解析自定义的xml文件,没有使用w3c组织所推荐的任何一种接口,自己实现对自定义xml文件的解析,因为能力不够,所以实现下基本原理,加深对xml文件解析原理的理解。  上一篇博客我发现都没写出来我对组合模式的理解,很尴尬,文笔不好呢。  先简略说
Wesley13 Wesley13
4年前
Java中23种设计模式详解
Java中23种设计模式1\.设计模式31.1创建型模式41.1.1工厂方法41.1.2抽象工厂61.1.3建造者模式101.1.4单态模式131.1.5原型模式151.2结构型模式171.2.1适配器模式171.2.2桥接模式191.2.3组合
Wesley13 Wesley13
4年前
Java Design Patterns
java的设计模式大体上分为三大类:创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模
Stella981 Stella981
4年前
Javascript继承5:如虎添翼
/寄生式继承其实就是对原型继承的第二次封装,在封装过程中对继承的对象进行了扩展。也存在原型继承的缺点!!这种思想的作用也是为了寄生组合式继承模式的实现。///声明基对象varbook{name:'jsbook',al
Wesley13 Wesley13
4年前
23种设计模式(面向对象语言)
一、设计模式的分类总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。创建型模式是用来创建对象的模式,抽象了实例化的过程,帮助一个系统独立于其他关联对象的创建、组合和表示方式。所有的创建型模式都有两个主要功能:  1.将系统所使用的具体类的信息封装起来  2.隐藏
Wesley13 Wesley13
4年前
Java描述设计模式(10):组合模式
本文源码:GitHub·点这里(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fcicadasmile%2Fmodelarithmeticparent)||GitEE·点这里(https://gitee.com/cicadasmile/modela
设计模式-单例模式概述 | 京东云技术团队
我们常把23种经典的设计模式分为三类:创建型、结构型、行为型,其中创建型设计模式主要解决“对象的创建”问题,将创建和使用代码解耦,结构型设计模式主要解决“类或对象的组合或组装”问题,将不同功能代码解耦,行为型设计模式主要解决“类或对象之间的交互”问题,将不
前端常用设计模式初探 | 京东云技术团队
设计模式一直是程序员谈论的“高端”话题之一,总有一种敬而远之的心态。在了解后才知道在将函数作为一等对象的语言中,有许多需要利用对象多态性的设计模式,比如单例模式、策略模式等,这些模式的结构与传统面向对象语言的结构大相径庭,实际上已经融入到了语言之中,我们可