设计模式(三):“花瓶+鲜花”中的装饰者模式(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 (0) 重新温习(序言)
几年前,参与过一个项目.那算是一个小型的信息管理系统,BS 结构的,前端用的是基于 jQuery 的 EasyUI 框架. 我进 Team 的时候,项目已经进入开发阶段半个多月了.听说整个项目的框架是 ...
- .NET 4.6.2正式发布带来众多特性
虽然大多数人的注意力都集中在.NET Core上,但与原来的.NET Framework相关的工作还在继续..NET Framework 4.6.2正式版已于近日发布,其重点是安全和WinForms/ ...
- Git Bash的一些命令和配置
查看git版本号: git --version 如果是第一次使用Git,你需要设置署名和邮箱: $ git config --global user.name "用户名" $ gi ...
- OpenSceneGraph in ActiveX by ActiveQt
OpenSceneGraph in ActiveX by ActiveQt eryar@163.com Abstract. Qt’s ActiveX and COM support allows Qt ...
- python爬取github数据
爬虫流程 在上周写完用scrapy爬去知乎用户信息的爬虫之后,github上star个数一下就在公司小组内部排的上名次了,我还信誓旦旦的跟上级吹牛皮说如果再写一个,都不好意思和你再提star了,怕你们 ...
- JavaScript实现常用的排序算法
▓▓▓▓▓▓ 大致介绍 由于最近要考试复习,所以学习js的时间少了 -_-||,考试完还会继续的努力学习,这次用原生的JavaScript实现以前学习的常用的排序算法,有冒泡排序.快速排序.直接插入排 ...
- jQuery学习之路(7)- 用原生JavaScript实现jQuery的某些简单功能
▓▓▓▓▓▓ 大致介绍 学习了妙味,用原生的JavaScript实现jQuery中的某些部分功能 定义自己的函数库lQuery ▓▓▓▓▓▓ $()选择器的实现 jQuery是面向对象的,所以自己编写 ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- Javascript 代理模式模拟一个文件同步功能
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 个人网站对xss跨站脚本攻击(重点是富文本编辑器情况)和sql注入攻击的防范
昨天本博客受到了xss跨站脚本注入攻击,3分钟攻陷--其实攻击者进攻的手法很简单,没啥技术含量.只能感叹自己之前竟然完全没防范. 这是数据库里留下的一些记录.最后那人弄了一个无限循环弹出框的脚本,估计 ...