设计模式(九):组合模式

设计模式(九):组合模式大家好 欢迎来到编程队伍 我是作者王小伍 你可以叫我伍先生这篇文章是设计模式系列文章的第九篇 组合模式设计模式系列前几篇没看的可以点击对应的文章快速查看设计模式 一 单例模式设计模式 二 工厂模式设计模式 三 生成器模式设计模式 四

大家好,欢迎来到IT知识分享网。

大家好,欢迎来到编程队伍,我是作者王小伍,你可以叫我伍先生

这篇文章是设计模式系列文章的第九篇:组合模式

设计模式系列前几篇没看的可以点击对应的文章快速查看

设计模式(一):单例模式

设计模式(二):工厂模式

设计模式(三):生成器模式

设计模式(四):原型模式

设计模式(五):适配器模式

设计模式(六):装饰器模式

设计模式(七):桥接模式

设计模式(八):代理模式


正文

我们还是老规矩,用一个具体案例开始我们的设计模式之旅

假如有一个公司,要统计这个公司里所有员工的年龄总和

我们先来看一下公司的人员组成结构

设计模式(九):组合模式

部门A下面有两个员工和一个部门B。部门B下面有两个员工

图中画的只有两个部门,每个部门下两个员工。实际情况是一个公司有很多部门,每个部门下有很多员工

想象一下,如果让某个人去统计全公司所有人的年龄的话,那对这个人来说工作量太大了

我们可以采取这样的方式,让每个部门去统计各自部门下的员工年龄的总和,然后再一层一层往上汇报

比如,部门B先统计它部门下员工的年龄总和上报给部门A;部门A再统计它部门下的直属员工的年龄加上部门B的员工年龄,汇总以后再向上汇报

根据以上思路可以抽象出我们的代码结构

设计模式(九):组合模式

一个公司接口类 Company,里面定义一个 getAge() 方法,用来获取员工年龄

然后是员工类 Employee 和部门类 Department,分别实现公司接口类,然后重写 getAge() 方法

下面开始写代码

首先是公司接口类

public interface Company { int getAge(); } 

员工类,这里只放员工A1的代码实现,其他员工的代码和这个一样

public class EmployeeA1 implements Company { private int age; public EmployeeA1(int age) { this.age = age; } @Override public int getAge() { return this.age; } } 

部门类,这里只放部门A的代码实现,部门B和这个一样

public class DepartmentA implements Company { private List<Company> list = new ArrayList<>(); private String name; public void add(Company company) { list.add(company); } @Override public int getAge() { // 统计部门下所有员工的年龄总和 int age = 0; for (Company company : list) { age = age + company.getAge(); } return age; } } 

在客户端调用之前要把公司的人员结构和年龄设置好

public static Company getCompany() { EmployeeA1 employeeA1 = new EmployeeA1(25); // 部门A下的1号员工 EmployeeA2 employeeA2 = new EmployeeA2(26); // 部门A下的2号员工 EmployeeB1 employeeB1= new EmployeeB1(30); // 部门B下的1号员工 EmployeeB2 employeeB2= new EmployeeB2(30); // 部门B下的2号员工 DepartmentB departmentB = new DepartmentB(); // 组装部门B departmentB.add(employeeB1); departmentB.add(employeeB2); DepartmentA departmentA = new DepartmentA(); // 组装部门A departmentA.add(employeeA1); departmentA.add(employeeA2); departmentA.add(departmentB);// 部门B也属于部门A return departmentA; } 

下面开始统计公司员工的年龄

Company company = getCompany(); // 获取公司的人员架构 System.out.println(company.getAge()); // 统计年龄 

最后输出的结果是 111,公司员工的年龄总和就统计出来了

其实,这就是组合模式一个具体实现

组合模式基本介绍

组合模式在特定场景下还是比较实用的,在创建型模式、结构型模式和行为型模式分类中,组合模式归属于结构型模式

组合模式也叫部分-整体模式、合成模式或对象树

如果要实现的功能的结构可以被抽象成树状结构,就非常适合使用组合模式

在我们上文例子中,员工就是树状结构的叶子节点,部门就是树状结构的树枝节点

组合模式的结构如下图

设计模式(九):组合模式

组合模式的实现方式主要分为三步

  1. 定义一个接口,并在接口中定义要实现的功能
  2. 叶子节点实现这个接口,并重写接口中的方法
  3. 树枝节点中有一个集合或者数组,可以对接口对象进行管理。同时,树枝节点还要实现这个接口,在重写接口的方法时可以循环集合或数组得到接口对象,并对其进行调用

通用模式和安全模式

组合模式又分为通用模式和安全模式,上文中使用的是安全模式

从它们的结构图可以看出它们的区别

设计模式(九):组合模式

图中标黄的部分就是透明模式和安全模式的主要区别

透明模式中,顶级类一般是抽象类,会定义所有的方法,通用的业务逻辑也会在这里面进行实现

在叶子节点或树枝节点中,根据自己的需要重写对应的方法

在叶子节点中,一般不会重写 add()remove()方法,这些方法往往都是空实现

调用者对叶子节点调用add()remove()方法时会出现运行异常,比较不安全

安全模式就不会出现这样的问题,因为在安全模式中 add()remove() 这一类对集合操作的方法都只会出现在树枝节点中,叶子节点没有这一类的方法

安全模式也有一定的缺点,没有把对象的所有行为抽象到接口类中,不符合依赖倒置原则。后续如果出现业务变动,会引起一系列的改变,不利于程序的扩展

组合模式优点

调用者调用简单,不管针对叶子节点还是树枝节点,调用者的调用逻辑是一样的

方便扩展,无论是新增叶子节点还是树枝节点,都可以很方便的扩展

组合模式缺点

对于跨度较大或很复杂的功能,进行树状结构抽象时会比较困难。对应的,在代码实现后,代码的理解成本也会比较高

在透明模式下,可能会出现运行异常

适用场景

如果要实现的核心功能可以抽象为树状结构,那么就该首先考虑使用组合模式

如果希望调用者,在针对叶子节点和树枝节点的调用逻辑上保持一致,可以考虑使用组合模式

与其他模式关系

  • 与装饰器模式对比

组合模式和装饰器模式类似,都是利用递归的方式来生成N多个对象

不同的是,装饰器模式的子类结构相同。而组合模式的子类分为叶子节点和树枝节点

装饰器模式为原对象增加了额外的功能,组合模式仅仅是对原对象进行的组合调用

装饰器模式回顾:设计模式(六):装饰器模式

  • 与其他模式对比

在设计模式中,有很多模式都和组合模式类似,都是将一部分工作委派给一个子类对象。

比如,组合模式中的树枝节点,生成器模式中的导演类,适配器模式中适配对象等

区别在于,子类对象要实现的功能是不一样的

最后还是那句话,设计模式不是万能的,只有合理利用设计模式才能写出合理的代码

— 文章来自公众号:编程队伍,转载请注明出处

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/185105.html

(0)
上一篇 2025-08-05 08:10
下一篇 2025-08-05 08:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信