今天让我们继续跟随《Head First 设计模式》的足迹,聊一个新的设计模式,有了它,你将能够在不修改任何底层代码的情况下,给你的(或别人的)对象赋予新的职责。首先,按照惯例,从故事说起——

星巴兹(Starbuzz)是以扩张速度最快而闻名的咖啡连锁店。因为扩张速度实在太快了,他们准备更新订单系统,以合乎他们的饮料供应要求。他们原先的类设计是这样的……

cost方法是计算咖啡价格的方法,description是描述咖啡的方法,比如描述咖啡加了什么调料。乍一看设计也是ok的,但是调料的增多,例如:蒸奶(Steamed Milk)、豆浆(Soy)、摩卡(Mocha,也就是巧克力风味)或覆盖奶泡。而且调料的价格的不同,就会出现一种情况,所谓类爆炸!

一种配置的咖啡一个类,对于星巴克这种级别的咖啡馆来说是令人奔溃的设计,而且增加调料种类还会修改起来让人疯掉。

其实完全没必要如此设计,可以在基类Beverage增加一些判断所添加的调料种类的布尔类型的成员变量和各种调料的价格就好了,然后在不同的咖啡的实现类中的cost根据那些判断的成员变量就可以计算出总的价格了。

设计如下:

这样只需要五个子类(五种饮料)就可以解决战斗了,不过怎么看都觉得别扭。

1.调料价钱的改变会使我们更改现有代码。
    2.一旦出现新的调料,我们就需要加上新的方法,并改变超类中的cost()方法。
    3.以后可能会开发出新饮料。对这些饮料而言(例如:冰茶),某些调料可能并不适合,但是在这个设计方式中,Tea(茶)子类仍将继承那些不适合的方法,例如:hasWhip()(加奶泡)。万一顾客想要双倍摩卡咖啡,怎么办?

还是那句话,这样的设计扩展性差。

于是你又打电话给了深山的大师,大师又抛出一句话:“牢记,代码应该如同晚霞中的莲花一样地关闭(免于改变),如同晨曦中的莲花一样地开放(能够扩展)。”其实大师想说的是一个设计原则:类应该对扩展开放,对修改关闭。(所谓“开放-关闭”原则)
     我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

这听起来有点不可思议,你很迷茫,正想再打电话给大师的时候,发现大师已经发了条短信给你:“可以使用装饰着模式”。

什么事装饰者模式呢?比较官方的说法如下:

装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

模式的框架图大致如下:

该模式的特点是:

1.装饰者和被装饰对象有相同的超类型。

2.你可以用一个或多个装饰者包装一个对象。
    3. 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,
可以用装饰过的对象代替它。
    4.装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。

5.对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰
对象。

6.具体应用于我的咖啡系统。在这里要采用不一样的做法:我们要以饮料为主体,然后在运行时以调料来“装饰”(decorate)饮料。

如何做到“用调料来装饰咖啡”呢?大致思想类似下图:

纯咖啡是中间的核心,每次加一种调料,就在外层加一层包装。

代码结构大致如下:

这里也运用到了继承,但是这么做的重点在于,装饰者和被装饰者必须是一样的类型,也就是有共同的超类,这是相当关键的地方。在这里,我们利用继承达到“类型匹配”,而不是利用继承获得“行为”。

想必各位听得仍然云里雾里的,来,上代码自然一切就清楚了:

咖啡基类Beverage:

调料装饰者基类:

必须继承Beverage,才完成装饰的功能。

两个咖啡实例:

咖啡Espresso:

咖啡HouseBlend:

调料,比如Mocha:

看到这里,各位应该知道是如何装饰了吧。其实很简单,就是通过构造器获得被装饰者的委托,然后在getDescription和cost方法获取被装饰者的描述和价格接着将其和本类对应的调料的描述和价格用类似拼接的方式合并在一起。

激动人心的时刻到了,进入运行代码了:

运行结果:

看,只要在运行时代码通过new对象传入适当的参数就可以任意创建不同调料组合的咖啡了。类的数量不大,增加咖啡或者调料只要增加相应的类即可,无需修改其他的类,价格的改变也只需在相应的类稍微修改下即可。是不是很爽?

装饰者模式在实际应用中最典型的例子就是java.io包的类。刚学java io的人一定会被io包中的类数量之庞大、功能之类似搞的一头雾水把。但是当我们知道了装饰者模式后,对于io包的理解就会简单清晰多了,因为Io包的类就是不断使用装饰者模式,一个一个类的装饰,为被装饰者添加新的功能,使用者根据需要去使用对应的类。

以下是一个简单的说明图:

祝各位学习快乐。

调侃《HeadFirst设计模式》之装饰者模式相关推荐

  1. Java设计模式(装饰者模式-组合模式-外观模式-享元模式)

    Java设计模式Ⅳ 1.装饰者模式 1.1 装饰者模式概述 1.2 代码理解 2.组合模式 2.1 组合模式概述 2.2 代码理解 3.外观模式 3.1 外观模式概述 3.2 代码理解 4.享元模式 ...

  2. 前端也要学系列:设计模式之装饰者模式

    什么是装饰者模式 今天我们来讲另外一个非常实用的设计模式:装饰者模式.这个名字听上去有些莫名其妙,不着急,我们先来记住它的一个别名:包装器模式. 我们记着这两个名字来开始今天的文章. 首先还是上< ...

  3. 设计模式 之 装饰者模式

    2019独角兽企业重金招聘Python工程师标准>>> 设计模式 之 装饰者模式 装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对 ...

  4. 【设计模式】装饰者模式 ( 概念 | 适用场景 | 优缺点 | 与继承对比 | 定义流程 | 运行机制 | 案例分析 )

    文章目录 I . 装饰者模式概念 II . 装饰者模式适用场景 III . 装饰者模式优缺点 IV . 装饰者模式与继承对比 V . 装饰者模式相关设计模式 VI . 装饰者模式四个相关类 VII . ...

  5. 设计模式学习----装饰器模式

    这两天本来是自在学习java collection Framework的Fail Fast底层机制,看到核心的部分时,突然意识到设计模式的问题,上大学到现在我还没有真正理解过设计模式的概念,于是用了大 ...

  6. 【设计模式】装饰器模式的使用

    问题来源 我们在进行软件系统设计的时候,有一些业务(如下图,一些通用的非功能性需求)是多个模块都需要的,是跨越模块的.把它们放到什么地方呢? 最简单的办法就是把这些通用模块的接口写好,让程序员在实现业 ...

  7. C#设计模式(9)——装饰者模式(Decorator Pattern)

    一.引言 在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类).A ...

  8. go设计模式之装饰器模式

    go设计模式之装饰器模式 再写这篇文章时,我已经看了很多其他人发表的类似文章,大概看了这么多吧. 亓斌的设计模式-装饰者模式(Go语言描述) jeanphorn的Golang设计模式之装饰模式 七八月 ...

  9. python中的装饰器、装饰器模式_python 设计模式之装饰器模式 Decorator Pattern

    #写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...

  10. python 设计模式之装饰器模式 Decorator Pattern

    #写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...

最新文章

  1. ef延迟加载不到导航属性问题
  2. Android TV 悬浮球模拟物理按键
  3. I.MX6 I2C DS1337 disable square-wave output
  4. 动态时间规整_动态规划-数组系列(10%)
  5. 2020年十月蓝桥杯A组题解【10月17日】【附完整代码】
  6. python判断输入的数字是完全平方还是三次方
  7. 牛逼!Python的判断、循环和各种表达式(长文系列第2篇
  8. 用VS2008做博客¥(^_^)¥
  9. html display失效,CSS3 中 transition-duration 对 display: none/block 属性无效?
  10. 用popen函数操作其它程序的输入和输出
  11. NHibernate中ISession的Flush
  12. Objective-C对象模型及应用
  13. pyside6(1):Qt 资源系统和qrc文件使用
  14. 面板数据熵值法-Python
  15. 近来开发工作不忙,零零散散整理的Java基础
  16. java 集合之HashMap 源码阅读记录
  17. 数字与字符串,,,字符串与字符串之间比较大小
  18. java sortmap分析_Java编程中的SortedMap接口
  19. 如何运行单个.vue文件
  20. 外刊逐句精读|《金融时报》:亚马逊的官方水军

热门文章

  1. 如何将文件夹打成jar包
  2. 【机器学习实战】利用朴素贝叶斯算法(naive_bayes)实现新闻分类
  3. 【CSS3盒子模型新样式】boder-box
  4. 完美解决python3.6环境下,使用pyinstaller打包.exe时报错的情况。
  5. 开源项目的版权声明已无存在必要?
  6. 信号复数及希尔伯特变换的理解
  7. 华为2017年8月30日校招编程真题01-数字的中文拼音和英文单词之间互相转换
  8. 【Golang】家庭收支记账软件
  9. 2500个常用汉字及繁体对应
  10. Shell 通过sed替换文件字符串