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
缺点
- 存在大量小类,使用时将会增加代码复杂度
P101
P104
- 使用时依赖某种特殊类型,然后忽然导入装饰器,却又没有周详地考虑一切
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设计模式之单例模 ...
随机推荐
- Java获取不到请求的真实IP
问题 最近在写博客浏览量的时候,设计了这么一个逻辑:同一个IP浏览一遍文章,5分钟内不刷新次数.就需要在服务器端得到用户的真实IP,我代码是这样写的(从网上找的方法): public static S ...
- 彻底搞懂js __proto__ prototype constructor
在开始之前,必须要知道的是:对象具有__proto__.constructor(函数也是对象固也具有以上)属性,而函数独有prototype 在博客园看到一张图分析到位很彻底,这里共享: 刚开始看这图 ...
- 题解 洛谷P6853 station
蒟蒻语 还是蒟蒻太菜了,这场 div1 竟然一题都没做出来/kk/kk/kk 蒟蒻解 首先我们把每 5 个点分为一组.然后分组结果大概是这样子: 可以看到首先下面需要有一条边来让整张图有一条支撑的路径 ...
- 题解-[HNOI2016]序列
题解-[HNOI2016]序列 [HNOI2016]序列 给定 \(n\) 和 \(m\) 以及序列 \(a\{n\}\).有 \(m\) 次询问,每次给定区间 \([l,r]\in[1,n]\),求 ...
- 关于微信NFC功能开发的链接总结
特此申明:若有侵权,请联系我,我会第一时间删除 一. 小程序开发一般流程: 首先调用 wx.getHCEState(OBJECT), 判断设备是否支持NFC,(ios,android兼容性处理) 调用 ...
- Jmeter(2)基础知识
一.Jmeter测试计划 1.测试计划用来描述一个性能/接口测试的脚本和场景设计 独立运行每个线程组:用于控制测试计划中的多个线程组的执行顺序.不勾选时,默认各线程组并行.随机执行. 主线程结束后运行 ...
- 访问控制列表ACL应用
ACL的应用的场景 应用在三层接口 • Nat地址转换 Nat(network address translation,地址转换)是将数据报报头中的ip地址转换为另一个ip地址的过程,主要用于实现内部 ...
- Error while instantiating 'org.apache.spark.sql.hive.HiveSessionStateBuilder': —— windows 开发环境使用spark 无法访问hdfs 问题解决
## 错误: ## 解决方案: 下载 hadoop 的可执行tar包,解压放在windows 本地,并配置环境变量. 在 解压后的文件夹的bin目录下放入两个文件: winutils.exe, had ...
- [OI笔记]每周刷题记录
一些题库: bzoj.uoj.luogu(洛谷).CF.loj.hdu.poj.51nod 下面是一些近期的做题记录 省选爆炸-然后大概就先这样了,要回去读一段时间文化课,如果文化课还不错的话也许还会 ...
- Ch2信息的表示和处理——caspp深入理解计算机系统
目录 第2章 信息的表示和处理 2.1 信息存储 2.1.1 十六进制 一.表示法 二.加减 三.进制转换 2.1.2 字 2.1.3 数据大小 2.1.4 字节顺序与表示 一.字节的排列规则 二.打 ...