Head First 设计模式 —— 03. 装饰器 (Decorator) 模式
思考题
有如下类设计:


如果牛奶的价钱上扬,怎么办?新增一种焦糖调料风味时,怎么办?
造成这种维护上的困难,违反了我们之前提过的哪种设计原则? P82
- 取出并封装变化的部分,让其他部分不收影响
- 多用组合,少用继承
思考题
请为下面类的 cost() 方法书写代码。 P83
抽象类:Beverage
public class Beverage {
public double cost() {
double totalCost = 0.0;
if (hasMilk()) {
totalCost += milkCost;
}
if (hasSoy()) {
totalCost += soyCost;
}
if (hasMocha()) {
totalCost += mochaCost;
}
if (hasWhip()) {
totalCost += whipCost;
}
return totalCost;
}
}
具体类:DarkRoast
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "Most Excellent Dark Roast";
}
public double cost() {
return baseCost + super.cost();
}
}
思考题
当哪些需求或因素改变时会影响这个设计? P84
- 调料价钱的改变会使我们改变现有代码
- 一旦出现新的调料,我们就需要加上新的方法,并改变超类中的 cost() 方法
- 以后可能会开发出新饮料。对这些饮料而言(例如:冰茶),某些调料可能并不适合,但是在这个设计方式中,Tea (茶)子类仍然将继承那些不适合的方法,例如:hasWhip() (加奶泡)
- 万一顾客想要双倍摩卡或咖啡,怎么办?
- 调料价钱随着具体饮料而改变
- 饮料基础价钱随着大中小被的不同而改变
设计原则
- 开闭原则:类应该对扩展开放,对修改关闭
P86- 策略模式、观察者模式和装饰器模式均遵循开闭原则
P105
- 策略模式、观察者模式和装饰器模式均遵循开闭原则
装饰器模式
动态地将责任附加到对象上,而不改变其原有代码。若扩展功能,装饰器提供了比继承更优弹性的替代方案。 P91

特点
- 装饰类和被装饰类有相同的超类型
P90 - 装饰类可以在所委托的被装饰类的行为之前(或之后),加上自己的行为,以达到特定的目的
P90 - 可以透明地插入装饰器,使用时甚至不需要知道是在和装饰器交互
P104 - 适合用来建立有弹性的设计,维持开闭原则
P104
缺点
- 存在大量小类,使用时将会增加代码复杂度
P101P104 - 使用时依赖某种特殊类型,然后忽然导入装饰器,却又没有周详地考虑一切
P104
思考题
我们在星巴兹的朋友决定开始在菜单上加上咖啡的容量大小,供顾客可以选择小贝(tall)、中杯(grande)、大杯(venti)。星巴兹认为这是任何咖啡都必须具备的,所以在 Beverage 类中加上了 getSize() 与 setSize() 。他们也希望调料根据咖啡容量收费,例如:小中大杯的咖啡加上豆浆,分别加收 0.10、0.15、0.20 美金。
如何改变装饰者类应对这一的需求? P99
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
public int getSize() {
return beverage.getSize();
}
public void setSize(int size) {
beverage.setSize(size);
}
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
public double cost() {
double soyCost = 0.0;
switch (getSize()) {
case TALL:
soyCost = 0.10;
break;
case GRANDE:
soyCost = 0.15;
break;
case VENTI:
soyCost = 0.20;
break;
default:
soyCost = 0.0;
}
return beverage.cost() + soyCost;
}
}
所思所想
- 装饰器模式使用了继承(或实现接口)的方式,所以超类型增加方法时,所有子类都需要改变,设计时要充分考虑
- 总感觉装饰器模式很熟悉,看了 java-design-patterns/decorator 后,发现装饰器模式的思想平常都是运用在 AOP 中实现的 (最后学了代理模式发现原来是动态代理模式)
本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/head-first-design-patterns
Head First 设计模式 —— 03. 装饰器 (Decorator) 模式的更多相关文章
- 装饰器(Decorator)模式
public interface IDoThings { public void doSomeThing(); } public class DoThings implements IDoThings ...
- java设计模式03装饰者者模式
动态地给一个对象添加一些额外的职责.就增加功能来说, Decorator模式相比生成子类更为灵活.该模式以对客 户端透明的方式扩展对象的功能. (1)在不影响其他对象的情况下,以动态.透明的方式给单个 ...
- 设计模式:装饰器(Decorator)模式
设计模式:装饰器(Decorator)模式 一.前言 装饰器模式也是一种非常重要的模式,在Java以及程序设计中占据着重要的地位.比如Java的数据流处理,我们可能看到数据流经过不同的类的包装和 ...
- python 设计模式之装饰器模式 Decorator Pattern
#写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...
- PHP设计模式之装饰器模式(Decorator)
PHP设计模式之装饰器模式(Decorator) 装饰器模式 装饰器模式允许我们给一个类添加新的功能,而不改变其原有的结构.这种类型的类属于结构类,它是作为现有的类的一个包装 装饰器模式的应用场景 当 ...
- Java设计模式07:常用设计模式之装饰器模式(结构型模式)
1. Java之装饰器模式(Decorator Pattern) (1)概述: 装饰模式在Java种使用也很广泛,比如我们在重新定义按钮.对话框等时候,实际上已经在使用装饰模式了.在不必改变原 ...
- JS 设计模式九 -- 装饰器模式
概念 装饰者(decorator)模式能够在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能. 模拟传统面向对象语言的装饰者模式 //原始的飞机类 var Plane = ...
- 设计模式之装饰(Decorator)模式
设计模式之装饰(Decorator)模式 (一)什么是装饰(Decorator)模式 装饰模式,又称为包装模式,它以对客户端透明的方式扩张对象的功能,是继承关系的替代方案之一. 装饰模式可以在不使用创 ...
- python设计模式之装饰器详解(三)
python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...
随机推荐
- SQL Injection (Blind) Low
SQL盲注分析 盲注较普通注入难度会有所增加,根据页面响应不同大概分为以下几种:布尔型盲注:时间盲注:报错注入 普通注入与盲注的对比: 普通注入: ...
- 斜率优化DP复习笔记
前言 复习笔记2nd. Warning:鉴于摆渡车是普及组题目,本文的难度定位在普及+至省选-. 参照洛谷的题目难度评分(不过感觉部分有虚高,提高组建议全部掌握,普及组可以选择性阅读.) 引用部分(如 ...
- 落谷 P4052 [JSOI2007]文本生成器
题目链接.只要有一个可读就行,容斥会好做一点. 可读数量 \(=\) 总数 \(-\) 不可读数量 总数显然是 \(26 ^ n\). 求解不可读数量 不可读数量可以利用 AC 自动机的模型进行 DP ...
- 快速上手微信小程序webSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议.WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范.WebSocket API也被W3 ...
- STL——容器(Set & multiset)的概念和特点
1. Set 和 multiset 的概念 set 和 multiset 是一个集合容器,其中 set 所包含的元素是唯一的,集合中的元素按一定的顺序排列.set 采用红黑树变体的数据结构实现,红黑树 ...
- 数据结构,哈希表hash设计实验
数据结构实验,hash表 采用链地址法处理hash冲突 代码全部自己写,转载请留本文连接, 附上代码 #include<stdlib.h> #include<stdio.h> ...
- jwt 简单基本使用加密解密
import jwt # 加密 encode_jwt=jwt.encode({'uid':'123'},'密钥123',algorithm='HS256') print(encode_jwt) # 解 ...
- 一个实现浏览器网页与本地程序之间进行双向调用的轻量级、强兼容、可扩展的插件开发平台—PluginOK中间件
通过PluginOK中间件插件平台(原名本网通WebRunLocal)可实现在网页中的JavaScript脚本无障碍访问本地电脑的硬件.调用本地系统的API及相关组件,同时可彻底解决ActiveX组件 ...
- Linux安装Mycat1.6.7.4并实现Mysql数据库读写分离简单配置
1. Mycat简介 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务.ACID.可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群 一 ...
- maven下载和配置
maven就是管理架包(jar)的一个工具类,是一个绝佳的构建工具 但是maven是需要下载和配置环境的,和jdk差不多 1.首先进入apache的官网 http://maven.apache.org ...
