• 作者:老汪软件技巧
  • 发表时间:2024-08-25 11:06
  • 浏览量:

如果单纯从字面上来看,我们很容易将“组合模式”和“组合关系”搞混。组合模式最初只是用于解决树形结构的场景,更多的是处理对象组织结构之间的问题。而组合关系则是通过将不同对象封装起来完成一个统一功能。

虽然组合模式并不常用,但是学习它的原理能够帮助我们获得更多复杂结构上的思考。比如,MySQL 的索引设计中就是用了 B+ 树算法的组合模式设计,极大地提升了数据查询时的性能。组合模式的原理很容易理解,但是在代码实现上却是反直觉的,如果一下子看不懂其原理,可以多看几遍,并结合例子来反复思考。

一、模式原理分析

组合模式的定义是:将对象组合成树形结构以表示整个部分的层次结构。组合模式可以让用户统一对待单个对象和对象的组合。

这个定义中有两个关键点:一个是用树形结构来分层,另一个是通过统一对待来简化操作。之所以要使用树形结构,其实就是为了能够在某种层次上进行分类,并且能够通过统一的操作来对待复杂的结构。比如,我们常见的公司统计多个维度的人员的工资信息,如果一个一个统计单人的工资信息会比较费时费力,但如果我们将人员工资信息按照组织结构构建成一棵“树”,那么按照一定的分类标示(比如,部门、岗位),就能快速找到相关人员的工资信息,而不是每次都要查找完所有人的数据后再做筛选。

下面我们就来看看组合模式的 UML 图:

从图中,我们可以看到组合模式中包含了三个关键角色。

组合模式最常见的结构就是树形结构,通过上面的三个角色就可以很方便地构建树形结构。我们可以结合现实中的例子来理解,比如,一个公司中有总经理,在总经理之下有经理、秘书、副经理等,而在经理之下则有组长、开发人员等,其结构图大致如下:

除了树形结构以外,组合模式中还有环形结构和双向结构(如下图),其中,环形结构和数据结构中的单向链表很相似,而双向结构其实就是 Spring 中 Bean 常用的结构。

接下来我们再来看下组合模式对应 UML 的代码实现:

//抽象组件
public abstract class Component{
     public abstract void operation();
}
//叶子节点
public class Leaf extends Component{
    @Override
    public void operation() {
        //叶子节点的操作放这里
    }
}
//组合节点
public class Node extends Component {
    private List myChildren;  //存放子节点列表
    @Override
    public void operation() {
        for (Component component: myChildren) {
            component.operation();
        }
    }
}

这段代码的实现看上去特别简单,但是这里有一个经常容易被我们搞混淆的地方,那就是 Node 中 operation() 方法下的 for 循环。很多时候,我们以为这个 for 循环是一个固定实现的代码,但其实这个理解是错的。实际上,这里的 for 循环想要表达的意思是,遍历组合节点下的所有子节点时才需要用到循环,而不是这里只能用循环。

从上面的分析中,我们会发现组合模式封装了如下变化:

_原理模式分析怎么写_什么是原理分析

所以说,组合模式本质上封装了复杂结构的内在变化,让使用者通过一个统一的整体来使用对象之间的结构。

二、使用场景分析

组合模式一般的使用场景有:

这里我们就拿其中的“订单信息”来举例。假设你正在开发一个新的商品订单系统(如下图),那你会如何计算每个订单的总费用呢?

从上面的简图可以看到,一个订单中可能通常会包含各类商品、发票等信息。在现实中,每个商品都会被放到一个快递盒中,然后小的盒子又可以被放入另一个更大的盒子中,以此类推,整个结构看上去像是一棵“倒过来的树”。

过去,我们在计算价格时的做法是:打开所有快递盒,找到每件商品,然后计算总价。但在程序中,你会发现这不是使用简单的 for 循环语句(一次打开盒子的动作)就能完成的,因为你还需要知道:对应的商品类别有哪些,这个订单使用了哪些商品,价格是多少,赠送的商品有哪些,要抵消多少价格……

而组合模式就是专门为需要反复计算或统计的场景而生的。

三、为什么要使用组合模式?

通过上面的原理和使用场景分析,我们还可以总结出使用组合模式的三个主要原因。

第一,希望一组对象按照某种层级结构进行管理,比如,管理文件夹和文件,管理订单下的商品等。树形结构天然有一种层次的划分特性,能够让我们自然地理解多个对象间的结构。

第二,需要按照统一的行为来处理复杂结构中的对象,比如,创建文件,删除文件,移动文件等。在使用文件时,我们其实并不关心文件夹和文件是如何被组织和存储的,只要我们能够正确操作文件即可,这时组合模式就能够很好地帮助我们组织复杂的结构,同时按照定义的统一行为进行操作。

第三,能够快速扩展对象组合。 比如,订单下的手机商品节点可以自由挂接不同分类的手机(品牌类的,如华为、苹果),并且还可以按照商品的特性(如,价格、图片、商家、功能等)再自由地挂接新的节点组合,而查找时可以从手机开始查找,不断增加节点类型,直到找到合适的手机商品。

四、组合模式的优缺点是什么?

使用组合模式主要有以下三大优点。

同样,组合模式也有一些缺点。