设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)
在前两篇博客中详细的介绍了"策略模式"和“观察者模式”,今天我们就通过花瓶与鲜花的例子来类比一下“装饰模式”(Decorator Pattern)。在“装饰模式”中很好的提现了开放关闭原则,即类应该对扩展开放对修改关闭。装饰者模式可以让我们在不对原来代码的修改的情况下对类进行扩展。这也好比我们往花瓶里插花,我们在插花的时候是不会对花瓶以及原来的话进行任何的修改,而只管将我们新的花添加进花瓶即可。这就是我们的装饰者模式。当然本篇博客中所采用的语言仍然是Swift语言。
装饰者模式,用另一种表达方式就是“对原有的物体进行装饰,给原有的物体添加上新的装饰品”。举个栗子,比如一个礼物,我们要对其进行包装,礼物是被装饰者(我们称为组件---Component),而包装盒以及包装盒上的花等等就是装饰品(我们成为装饰者---Decorator)。如果换成花瓶与鲜花的关系,花瓶就是Component,而鲜花就是Decorator。下方引用了装饰者模式的定义:
装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰着提供了比继承更有弹性的替代方案。
一、使用“类图”分析鲜花+花瓶的装饰关系
与之前博客的风格类似,我们还是依托于实例来理解“装饰者模式”,我们就依托于花瓶与鲜花的关系来理解一下装饰者模式。在之前的博客中我们提到过一条设计原则“封装变化”,也就是说要将变化的东西进行封装提取。在“装饰者模式”中所使用的装饰就是变化的部分,也就是Decorator是变化的部分对应着我们的鲜花,因为往花瓶中插花的过程就是鲜花变化的过程,也就是为花瓶装饰的过程。而花瓶就是组件了。
在“装饰者模式”中需要注意的是,这里所谓的装饰者不单单就是我组件添加的新的装饰品。一个装饰者对象就是添加该装饰后的组件,也就是说装饰者=旧组件 + 新装饰品,理解这一点是非常重要的。具体请看下方的组件与装饰者之间的关系:
下方的类图就是我们将要实现的“装饰者模式”的实例,也就是鲜花和花瓶的关系。下方所有类的基类是VaseComponent(花瓶组件),在VaseComponent类中的description字段是用来描述某某花瓶中装有某某花的,display()方法用来打印description描述信息的。上方的红框就是所有的鲜花(装饰者---Decorator),所有鲜花装饰者的基类是FlowerDecorator,当然FlowerDecorator也是继承自VaseComponent(花瓶组件)的,因为装饰者拥有被装饰的对象同时又有新添加的装饰物(见上图)。在这些装饰者类中包含一个字段,该字段就是VaseComponent的对象(花瓶组件的对象)。该对象可以指没有任何装饰的花瓶,也可以指已经添加了装饰的花瓶。无论是装饰者还是被装饰者都有共同的基类,所以我们就可以利用多态来实现“装饰者模式”。
在上面类图的FlowerDecorator中的VaseComponent对象就对应着本部分第一张图中装饰者中包括的那个组件。该组件是最近一次装饰过的组件,而装饰者所负责的事情就是在该组件上添加上该装饰者特有的装饰品。换句话说,此时的装饰者的对象就是最新的组件对象。也许经过这些原理的讲解,你会有些迷惑,那么不要着急,在具体代码实现时会带你拨开云雾见日出的。
二、“花瓶+鲜花”具体代码实现(Swift版)
当然,我们此处的代码实现与上面的“类图”的设计是一致的。看完代码再结合着上面的“类图”你会对装饰者模式有更好的理解。下方我们会一步步的给出代码具体实现,当然下方的类名,成员变量以及成员方法的命名与上述类图一直。
1.实现空花瓶的基类(VaseComponent)
花瓶的基类VaseComponent就是我们被修饰者也就是我们所有花瓶(组件)的基类了。当然VaseComponent不仅仅是所有花瓶的基类,它还是所有装饰者的基类,因为装饰者对象 = 旧组件 + 新装饰品 = 新组件。在该类中的description字段中存储的是花瓶的描述信息,比如“瓷花瓶”,“玻璃花瓶”等信息。getDescription()->String方法是用来获取description存储的描述信息的。display()->Void方法就是对getDescription()方法获取到的值进行打印, 具体实现如下所示。
2.创建我们的空花瓶
在第一步中我们创建了空花瓶的基类,紧接着我们要实现具体的花瓶。在下方代码中我们创建了两个空花瓶,一个是Porcelain瓷花瓶,一个是Glass玻璃花瓶。并且在调用父类初始化器时为父类中的description字段进行初始化。空花瓶比较简单,代码也不多,空花瓶就是一个坯子,等着其他鲜花来做修饰,具体实现如下所示。
3. 鲜花基类(FlowerDecorator)的实现
FlowerDecorator也就是所有装饰者的基类,这里与其称为鲜花基类,还不如成为所有“新花瓶”的基类。什么是“新花瓶”呢?我们暂且成为添加了新的鲜花种类的花瓶为“新花瓶”。FlowerDecorator是所有鲜花装饰者的基类,而FlowerDecorator继承自VaseComponent类。在FlowerDecorator中添加了一个vase字段,该字段是VaseComponent类型,用于存储“旧组件”,也就是上一次被修饰过的花瓶组件。这也是所有装饰者都包含的字段,“装饰者”在初始化时会指定上次被修饰后组件(空花瓶或者其他修饰者的对象)。也就是说vase字段中存储的可以是一个空的花瓶对象,也可以是其他“装饰者”类的对象。具体实现如下所示:
4.实现各个装饰者(Decorator)
上方我们已经创建好了装饰者的基类,在装饰者基类中含有最新的组件(花瓶的状态,还有多少种类的花)。而在“装饰者”的类中我们要将具体花的品种,也就是我们变化的部分添加进具体的装饰者的实现中。下方的第一个类是我们的玫瑰花Rose类,重写了基类的getDescription()方法,在该方法中,为上一个装饰者添加了新的装饰品,也就是“玫瑰”。而在百合花Lily的类中我们为组件添加了“百合”装饰品。具体实现方式如下所示:
三、“万事俱备,只欠东风”--创建测试用例
经过上面的两大步,我们的装饰者模式的代码实现也就做完了。但是上面只是实现,没有测试用例的驱动,上面的示例看上去不够直观。为了搞清楚其工作方式,我们的测试用例还是必不可少的。下方就是我们的测试用例的代码:
在上述代码中呢,我们首先创建了一个空的瓷花瓶的对象procelain,紧接着打印描述信息(输出“瓷花瓶”)。然后为该瓷花瓶的对象procelain添加上Rose和Lily装饰。当然我们仍然使用procelain变量来接收添加Rose修饰后的对象(也就是Rose类的对象),此时Rose类的对象代表着“插有玫瑰花的瓷花瓶”。紧接着,我们在Rose对象的基础上添加了Lily装饰,添加Lily装饰后,porcelain就是Lily类的对象,表示“插有玫瑰花,百合花的瓷花瓶”。最后调用display()方法打印最新的描述信息。
在上述测试用例中,我们为porcelain对象添加了两个装饰品,最终的porcelain对象是Lily的对象,它是空瓷瓶+玫瑰+百合花的组合体。当最终该测试用例的Lily对象porcelain调用display()方法时,在display()方法中会调用该对象中的getDescription()方法,而该对象中的getDescription()方法会调用上一个修饰者(此处是Rose)对象的getDescription()方法,最终会找到我们的组件,也就是我们的空瓶子(Porcelain)中的的getDescription()方法。具体调用方式如下图所示:
今天关于“装饰者模式”的完整实例就先到这。
同样,在本篇博客的末尾,我们给出类本篇博客是Dmeo, Github: https://github.com/lizelu/DesignPatterns-Swift
设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)的更多相关文章
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- 设计模式学习--装饰者模式(Decorator Pattern)
概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...
- 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法
装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...
- 设计模式 - 装饰者模式(Decorator Pattern) 具体解释
装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...
- C#设计模式——装饰者模式(Decorator Pattern)
一.例子在软件开发中,我们往往会想要给某一类对象增加不同的功能.比如要给汽车增加ESP.天窗或者定速巡航.如果利用继承来实现,就需要定义无数的类,Car,ESPCar,CCSCar,SunRoofCa ...
- 设计模式(三)——装饰器模式(Decorator Pattern)
发现太过于刻意按照计划来写博客,有点不实际,刚好最近在一个网课上复习AOP的知识,讲到了装饰器模式和代理模式,顺便复习总结一下. 首先了解一下装饰器模式,从名字里面可以看出来,装饰器模式就类似于房子装 ...
- 23种设计模式之装饰器模式(Decorator Pattern)
装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包 ...
- Android设计模式之中的一个个样例让你彻底明确装饰者模式(Decorator Pattern)
导读 这篇文章中我不会使用概念性文字来说明装饰者模式.由于通常概念性的问题都非常抽象.非常难懂.使得读者非常难明确究竟为什么要使用这样的设计模式.我们设计模式的诞生,肯定是前辈们在设计程序的时候遇到了 ...
- python 设计模式之装饰器模式 Decorator Pattern
#写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...
随机推荐
- 前端框架 EasyUI (2)页面布局 Layout
在 Web 程序中,页面布局对应用程序的用户体验至关重要. 在一般的信息管理类的 Web 应用程序中,页面结构通常有一个主工作区,然后在工作区上下左右靠近边界的区域设置一些边栏,用于显示信息或放置一些 ...
- 谈谈如何使用Netty开发实现高性能的RPC服务器
RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...
- DynamicObject - 代理对象的种类
开箱即用,DynamicProxy提供了多种代理对象,主要分成两个大类: 基于继承(Inheritance-based) 基于继承的代理是通过继承一个代理类来实现,代理拦截对类的虚(virtual)成 ...
- HTML5 progress和meter控件
在HTML5中,新增了progress和meter控件.progress控件为进度条控件,可表示任务的进度,如Windows系统中软件的安装.文件的复制等场景的进度.meter控件为计量条控件,表示某 ...
- EventBus实现activity跟fragment交互数据
最近老是听到技术群里面有人提出需求,activity跟fragment交互数据,或者从一个activity跳转到另外一个activity的fragment,所以我给大家介绍一个开源项目,EventBu ...
- spring源码分析之freemarker整合
FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页.电子邮件.配置文件.源代码等)的通用工具. 它不是面向最终用户的,而是一个Java类库,是一款程 ...
- 解构C#游戏框架uFrame兼谈游戏架构设计
1.概览 uFrame是提供给Unity3D开发者使用的一个框架插件,它本身模仿了MVVM这种架构模式(事实上并不包含Model部分,且多出了Controller部分).因为用于Unity3D,所以它 ...
- 代码的坏味道(14)——重复代码(Duplicate Code)
坏味道--重复代码(Duplicate Code) 重复代码堪称为代码坏味道之首.消除重复代码总是有利无害的. 特征 两个代码片段看上去几乎一样. 问题原因 重复代码通常发生在多个程序员同时在同一程序 ...
- 用MongoDB分析合肥餐饮业
看了<从数据角度解析福州美食>后难免心痒,动了要分析合肥餐饮业的念头,因此特地写了Node.js爬虫爬取了合肥的大众点评数据.分析数据库我并没有采用MySQL而是用的MongoDB,是因为 ...
- java观察者模式
像activeMQ等消息队列中,我们经常会使用发布订阅模式,但是你有没有想过,客户端时如何及时得到订阅的主题的信息?其实就里就用到了观察者模式.在软件系统中,当一个对象的行为依赖于另一个对象的状态 ...