《Head First设计模式》第九章(2)组合模式
组合模式
基于前一篇迭代模式的案例进行需求更新,餐厅的菜单管理系统需要有煎饼屋菜单和披萨菜单。现在希望在披萨菜单中能够加上一份餐后甜点的子菜单。
在迭代模式中,披萨菜单是用数组维护的,我们需要让披萨菜单持有一份子菜单,但是不能真的把他赋值给菜单项数组,因为类型不同,所以不能这么做。
所以,需要重新实现煎饼屋菜单和披萨菜单了。事实是,我们已经到达了一个复杂级别,如果现在不重新设计,就无法容纳未来增加的菜单或子菜单的需求。我们需要一下改变:
- 需要某种树形结构,可以容纳菜单、子菜单和菜单项;
- 需要确定能够在每个菜单的各个项之间游走,而且至少像用迭代器一样方便;
- 需要能够更有弹性地在菜单项之间游走。比方说,可能只需要遍历甜点菜单,或者可以便利整个菜单;
我们首先想到的是采用树形结构:
我们要使用组合模式来解决这个问题,但并没有放弃迭代器模式,它仍然是解决方案中的一部分,然而管理菜单的问题已经到了一个迭代器无法解决的新维度。所以,我们将倒退几步,使用组合模式来解决。
组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象。使用组合结构,我们能把相同的操作应用在组合的个别对象上,换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。
定义
组合模式允许将对象组合成属性结构来表现“整体/部分”层次结构,组合能让客户以一致的方式处理个别对象以及对象组合。
组合模式能创建一个树形结构
我们要如何将组合模式利用在菜单上呢?一开始,我们需要创建一个组件接口来作为菜单和菜单项的共同接口,让我们能够用同意的做法来处理菜单和菜单项。来看看设计的类图:
菜单组件MenuComponent提供了一个接口,让菜单项和菜单共同使用。因为我们希望能够为这些方法提供默认的实现,所以我们在这里可以把MenuComponent接口换成一个抽象类。在这个类中,有显示菜单信息的方法getName()等,还有操纵组件的方法add(),remove(),getChild()等。
菜单项MenuItem覆盖了显示菜单信息的方法,而菜单Menu覆盖了一些对他有意义的方法。
具体来看看代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
|
组合模式以单一责任设计原则换取透明性。通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将组合和叶节点一视同仁。也就是说,一个元素究竟是组合还是叶节点,对客户是透明的。
现在,我们在MenuComponent类中同时具有两种类型的操作。因为客户有机会对一个元素做一些不恰当或是没有意义的操作,所以我们失去了一些安全性。
扩展:组合迭代器
我们现在再扩展一下,这种组合菜单如何设计迭代器呢?细心的朋友应该观察到,我们刚才使用的迭代都是递归调用的菜单项和菜单内部迭代的方式。现在我们想设计一个外部迭代的方式怎么办?譬如出现一个新需求:服务员需要打印出蔬菜性质的所有食品菜单。首先,我们给MenuComponent加上判断蔬菜类食品的方法,然后在菜单项中进行重写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
这个CmpositeIterator是一个不可小觑的迭代器,它的工作是遍历组件内的菜单项,而且确保所有的子菜单(以及子子菜单……)都被包括进来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
在我们写MenuComponent类的print方法的时候,我们利用了一个迭代器遍历组件内的每个项,如果遇到的是菜单,我们就会递归地电泳print方法处理它,换句话说,MenuComponent是在“内部”自行处理遍历。
但是在上页的代码中,我们实现的是一个“外部”的迭代器,所以有许多需要追踪的事情。外部迭代器必须维护它在遍历中的位置,以便外部可和可以通过hasNext和next来驱动遍历。在这个例子中,我们的代码也必须维护组合递归结构的位置,这也就是为什么当我们在组合层次结构中上上下下时,使用堆栈来维护我们的位置。
空迭代器
菜单项没什么可以遍历的,那么我们要如何实现菜单项的createIterator()方法呢。
1:返回null。我们可以让createIterator()方法返回null,但是如果这么做,我们的客户代码就需要条件语句来判断返回值是否为null;
2:返回一个迭代器,而这个迭代器的hasNext()永远返回false。这个是更好的方案,客户不用再担心返回值是否为null。我们等于创建了一个迭代器,其作用是“没作用”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
以上便是组合模式的一些内容。
《Head First设计模式》第九章(2)组合模式相关推荐
- 《HeadFirst设计模式》第九章-2组合模式
1.声明 设计模式中的设计思想.图片和部分代码参考自<Head First设计模式>,作者Eric Freeman & Elisabeth Freeman & Kathy ...
- Head First设计模式读书笔记八 第九章下 组合模式
之前的总结链接: https://blog.csdn.net/u011109881/article/details/58710579 对比headFirst书中的例子,我觉得书中的组合模式的例子比上面 ...
- Java设计模式(8)组合模式(Composite模式)
Composite定义:将对象以树形结构组织起来,以达成"部分-整体" 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. Composite比较容易理解,想到Compo ...
- 设计模式十二之组合模式
设计模式十二之组合模式 1. 模式的定义与特点 1.1 模式的定义 1.2 模式的特点 1.3 模式的使用场景 2. 模式的结构与实现 2.1 模式的结构 2.2 模式的实现 3. 模式在开源软件中的 ...
- 设计模式——(12)组合模式
第十二章 组合模式 1.1 引言 餐厅都有自己的一份菜单,而菜单由菜单项组成,每个菜单项描述菜名.菜价和描述等信息. 而在一份菜单中,可能包含子菜单,例如:餐厅菜单除了包含菜名,还包含一份甜品子菜单. ...
- 设计模式的理解:组合模式 (Composite)
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来表示部分以及整体层次.这种类型的设计模式属于结构型模式, ...
- 设计模式08: Composite 组合模式(结构型模式)
Composite 组合模式(结构型模式) 对象容器的问题 在面向对象系统中,我们常会遇到一类具有"容器"特征的对象--即他们在充当对象的同时,又是其他对象的容器. public ...
- 大战设计模式【13】—— 组合模式
组合模式(Composite) 设计模式使用的例子https://github.com/LinkinStars/DesignPatternsAllExample 一.定义 组合多个对象形成树形结构以表 ...
- 设计模式笔记九:组合模式
原文:http://www.runoob.com/design-pattern/ 少许个人理解,如有错误请指出.欢迎一起讨论. 组合模式(Composite Pattern) 又叫部分整体模式,是用于 ...
- 设计模式之禅【组合模式】
真刀实枪之组合模式 从公司的人事架构谈起吧 公司的组织架构 从上图中可以分析出: 有分支的节点(树枝节点) 无分支的节点(叶子节点) 根节点(无父节点) 有了树状结构图,再看看类图长啥样吧! 这个类图 ...
最新文章
- 重磅,武汉大学获捐10亿元!
- qt 串口粘包_Qt Socket 传输图片——图像拆包、组包、粘包处理
- Django登录验证——原生表单验证
- 计算机基础教程7 - 数字系统
- 1110 Complete Binary Tree (25 分)(搜索)
- python猜数字游戏
- 牛顿迭代公式(详细)
- P3373 【模板】线段树1和P3373 【模板】线段树 2
- 【Quectel移远展锐平台5G模组RX500U/RG200U使用指南(三)-PCIE】
- 手机端页面的自适应设计的开发思路
- 应对考试的计算程序复杂度。。。欸
- 小程序城市按首字母排序(如同苹果手机通讯录一样得效果)
- centos 关机和重启命令
- H3C产品的默认密码是多少?
- Unity3d 屏幕空间人体皮肤知觉渲染次表面散射Screen-Space Perceptual Rendering Subsurface Scattering of Human Skin
- 比较全面的随机森林算法总结
- 环保在线监测系统,网页版,提供源码
- 【调剂】211北京邮电大学2020年软件学院硕士研究生招生缺额信息
- 在职阿里8年,一个31岁女软件测试工程师的心声
- [高项]行政收尾VS合同收尾
热门文章
- python清空列表_Python之列表
- JAVA写同步栈_tomcat实现的同步队列和同步栈
- ubuntu默认root密码
- docker访问宿主机mysql_docker容器内访问宿主机127.0.0.1服务
- 【转】用fo-dicom实现print scu的注意事项!!!!!!!!!
- 【转】01Teams的前世今生
- ASP.Net请求处理机制初步探索之旅 - Part 3 管道
- 认识ASP.NET 5项目结构和项目文件xproj
- 26享元模式(Flyweight Pattern)
- 第五节: Quartz.Net五大构件之Trigger的四大触发类