这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战
今天我们一起来学习下并不常用,但是学习它的原理能够帮助我们获得更多复杂结构上的思考的一种模式----组合模式。多唠叨几句,我本月将会对java的设计模式精讲,欢迎点击头像,关注我的专栏,我会持续更新,加油!
持续更新中......
话不多说,进入正题
组合模式
组合模式相对容易理解,比如,MySQL 的索引设计中就是用了 B+ 树算法的组合模式设计,极大地提升了数据查询时的性能。
它的官方定义:将对象组合成树形结构以表示整个部分的层次结构。组合模式可以让用户统一对待单个对象和对象的组合。
注意:这个定义中有两个关键点:一个是用树形结构来分层,另一个是通过统一对待来简化操作,这就是组合模式的核心所在
们常见的公司统计多个维度的人员的工资信息,这里主要分部门、岗位,如果一个一个统计单人的工资信息会比较费时费力,但如果我们将人员工资信息按照组织结构构建成一棵“树”,那么按照一定的分类标示(比如,部门、岗位)
下面看张题:
从图中,我们可以看到组合模式中包含了三个关键角色:
-
抽象组件:定义需要实现的统一操作。
-
组合节点:代表一个可以包含多个节点的复合对象,意味着在它下面还可以有其他组合节点或叶子节点。
-
叶子节点:代表一个原子对象,意味着在它下面不会有其他节点了
组合模式最常见的结构就是树形结构,通过上面的三个角色就可以很方便地构建树形结构。我们可以结合现实中的例子来理解,比如,一个公司中有总经理,在总经理之下有经理、秘书、副经理等,而在经理之下则有组长、开发人员等,其结构图大致如下:
是不是一目了然。这就是树形,这就是组合。
下面我们根据上面的图看下代码
代码展示
//抽象组件
public abstract class Component{
public abstract void operation();
}
//叶子节点
public class Leaf extends Component{
@Override
public void operation() {
//叶子节点的操作放这里
}
}
//组合节点
public class Node extends Component {
private List<Component> myChildren; //存放子节点列表
@Override
public void operation() {
for (Component component: myChildren) {
component.operation();
}
}
}
这段代码的实现看上去特别简单,但是这里有一个经常容易被我们搞混淆的地方,那就是 Node 中 operation() 方法下的 for 循环。很多时候,我们以为这个 for 循环是一个固定实现的代码,但其实这个理解是错的。实际上,这里的 for 循环想要表达的意思是,遍历组合节点下的所有子节点时才需要用到循环,而不是这里只能用循环。
从上面的分析中,我们会发现组合模式封装了如下变化:
-
叶子节点和组合节点之间的区别;
-
真实的数据结构(树、环、网等);
-
遍历真实结构的算法(比如,广度优先,还是深度优先等);
-
结构所使用的策略(比如,用来汇总数据,还是寻找最佳/最坏的节点等)。
OK。今天的组合模式就讲到这里 我们下面来进行总结下,实际场景代码我还没有想到好的的场景代码,后面我会贴出来,大家可以关注我,干货会持续输出
总结
看下使用组合模式的优点:
-
第一,希望一组对象按照某种层级结构进行管理,比如,管理文件夹和文件,管理订单下的商品等。树形结构天然有一种层次的划分特性,能够让我们自然地理解多个对象间的结构。
-
第二,需要按照统一的行为来处理复杂结构中的对象,比如,创建文件,删除文件,移动文件等。在使用文件时,我们其实并不关心文件夹和文件是如何被组织和存储的,只要我们能够正确操作文件即可,这时组合模式就能够很好地帮助我们组织复杂的结构,同时按照定义的统一行为进行操作。
-
第三,能够快速扩展对象组合。 比如,订单下的手机商品节点可以自由挂接不同分类的手机(品牌类的,如华为、苹果),并且还可以按照商品的特性(如,价格、图片、商家、功能等)再自由地挂接新的节点组合,而查找时可以从手机开始查找,不断增加节点类型,直到找到合适的手机商品。
缺点还是有的。大家使用的时候注意下:
1、需要增加很多运行时的检查,增加了代码复杂度。
2、如果遇到错误的遍历算法可能会影响系统性能。