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设计模式之单例模 ...
随机推荐
- PyQt(Python+Qt)学习随笔:model/view架构中的排序和代理模型QSortFilterProxyModel
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.概述 在Model/View体系架构中,有两种方法可以进行排序:选择哪种方法取决于底层模型. 如 ...
- 第12.4节 Python伪随机数数生成器random模块导览
random模块实现了各种分布的伪随机数生成器,常用功能包括: random.seed(a=None, version=2):初始化随机数生成器,如果 a 被省略或为 None ,则使用当前系统时间. ...
- PyQt(Python+Qt)学习随笔:QListView的layoutMode属性和batchSize属性
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 batchSize属性 该属性是在layoutMode属性设置为Batched时,用于控制每个批量的 ...
- 网鼎杯 fakebook
这道题目登录之后我们可以看到有join和login login即登录,join即注册 我们通过查看robots.txt可以知道 有源代码泄露. 先将泄露的源码下载下来审计一波 <?php cla ...
- C#声明一个100大小的数组 随机生成1-100之间不重复的数
面试题:C#声明一个100大小的数组 随机生成1-100之间不重复的数下面是C#的实现方式,编译测试通过 public static void InsertRandomArray() { int[] ...
- CF873D Merge Sort
其实最优的方法其他的题解已经讲得很好了,本题解仅用于记录和分享一个新的思路. 这道题是让你输出符合条件的序列,而序列的每个数之间具有一定的逻辑关系,很容易想到拓扑排序,于是此题就变为,如何找出满足条件 ...
- selenium.common.exceptions.WebDriverException: Message: 'chromedriver'解决
相信很多第一次学习selenium的同学们也对这个异常不陌生了,但具体该如何解决这个bug呢? 主要的原因还是因为selenium模拟的客户端对浏览器的操作,但相应浏览器的驱动版本不匹配导致的. 为了 ...
- 将命令行提示符里的执行结果导出到text文件中
为便于查看和保存命令行提示符里的执行结果, 可以使用 ">" 将执行结果导入到指定.txt文件中. 例如: 在命令行提示符里查看C盘文件,并将结果导入到E盘dir-c-out ...
- 迭代 可迭代对象 迭代器的bj
1.迭代的概念 迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果.每一次对过程的重复称为一次"迭代",而每一次迭代得到的结果会作为下一次迭代的初始值. 2.可迭代对象 ...
- Kafka服务器后台启动
nohup bin/kafka-server-start.sh config/server.properties 1>/dev/null 2>&1 &
