Java设计模式の装饰者模式
目录
一、问题引入
二、设计原则
三、用装饰者模式解决问题
四、装饰者模式的特点
五、装饰者模式的定义
六、装饰者模式的实现
七、java.io包内的装饰者模式
一、问题引入
咖啡店的类设计:
一个饮料基类,各种饮料类继承这个基类,并且计算各自的价钱。
饮料中需要加入各种调料,考虑在基类中加入一些布尔值变量代表是否加入各种调料,基类的cost()中的计算各种调料的价钱,子类覆盖cost(),并且在其中调用超类的cost(),加上特定饮料的价钱,计算出子类特定饮料的价钱。
缺点:类数量爆炸、基类加入的新功能并不适用于所有的子类、调料价钱的改变、新调料的出现都会要求改变现有代码;有的子类并不适合某些调料等情况……
二、设计原则
类应该对扩展开放,对修改关闭。
我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。
如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。
要让OO设计同时具备开放性和关闭性,不是一件容易的事,通常来说,没有必要把设计的每个部分都这么设计。
遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度。
我们需要把注意力集中在设计中最有可能改变的地方,然后应用开放-关闭原则。
三、用装饰者模式解决问题
解决咖啡店饮料问题的方法:
以饮料为主体,然后在运行时以调料来“装饰”饮料。
比如,顾客想要摩卡(Mocha)和奶泡(Whip)深焙咖啡(DarkRoast):
DarkRoast继承自Beverage,有一个cost()方法。
第一步,以DarkRoast对象开始;
第二步,顾客想要摩卡,所以建立一个Mocha装饰者对象,并用它将DarkRoast对象包装(wrap)起来;
第三步,顾客想要奶泡,所以建立一个Whip装饰者对象,并用它将Mocha对象包起来;(Mocha和Whip也继承自Beverage,有一个cost()方法);
最后,为顾客算钱,通过调用最外圈装饰者(Whip)的cost()就可以。Whip()的cost()会先委托它装饰的对象(Mocha)计算出价钱,然后在加上奶泡的价钱。Mocha的cost()也是类似。
四、装饰者模式的特点
装饰者和被装饰对象有相同的超类型。
可以用一个或多个装饰者包装一个对象。
因为装饰者和被装饰者具有相同的类型,所以任何需要原始对象的场合,可以用装饰过的对象代替。
装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。
五、装饰者模式的定义
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
六、装饰者模式的实现
实现类图如下:

装饰者和被装饰者具有共同的超类,利用继承达到“类型匹配”,而不是利用继承获得“行为”;将装饰者和被装饰者组合时,加入新的行为。
详细源码:
package com.blankjor.decorator; /**
* @desc 抽象饮料类(抽象组件)
* @author Blankjor
* @date 2017年5月14日 上午11:14:06
*/
public abstract class Beverage {
String description = "Unkown Beverage"; public String getDescription() {
return description;
} /**
* 抽象价格计算方法
*
* @return
*/
public abstract double cost();
} package com.blankjor.decorator; /**
* @desc 浓缩饮料 (具体组件)
* @author Blankjor
* @date 2017年5月14日 上午11:20:11
*/
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
} @Override
public double cost() {
return 1.99;
} } package com.blankjor.decorator; /**
* @desc House Blend 饮料(具体组件)
* @author Blankjor
* @date 2017年5月14日 上午11:20:11
*/
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend";
} @Override
public double cost() {
return .20;
} } package com.blankjor.decorator; /**
* @desc 抽象装饰者类
* @author Blankjor
* @date 2017年5月14日 上午11:17:12
*/
public abstract class CondimentDecorator extends Beverage {
/**
* 为了后面的调料都能够获取到自己调料的描述
*/
public abstract String getDescription(); } package com.blankjor.decorator; /**
* @desc Mocha调料(具体装饰者)
* @author Blankjor
* @date 2017年5月14日 上午11:23:36
*/
public class Mocha extends CondimentDecorator {
Beverage beverage; public Mocha(Beverage beverage) {
this.beverage = beverage;
} @Override
public String getDescription() {
return beverage.getDescription() + ",Mocha";
} @Override
public double cost() {
return .20 + beverage.cost();
}
} package com.blankjor.decorator; /**
* @desc Soy调料(具体装饰者)
* @author Blankjor
* @date 2017年5月14日 上午11:20:11
*/
public class Soy extends CondimentDecorator {
Beverage beverage; public Soy(Beverage beverage) {
this.beverage = beverage;
} @Override
public String getDescription() {
return beverage.getDescription() + ",Soy";
} @Override
public double cost() {
return .60 + beverage.cost();
}
} package com.blankjor.decorator; /**
* @desc Whip调料(具体装饰者)
* @author Blankjor
* @date 2017年5月14日 上午11:20:11
*/
public class Whip extends CondimentDecorator {
Beverage beverage; public Whip(Beverage beverage) {
this.beverage = beverage;
} @Override
public String getDescription() {
return beverage.getDescription() + ",Whip";
} @Override
public double cost() {
return .40 + beverage.cost();
}
} package com.blankjor.decorator; /**
* @desc 测试装饰者模式
* @author Blankjor
* @date 2017年5月14日 上午11:29:50
*/
public class MainTest {
public static void main(String[] args) {
// 创建一种调料
Beverage beverage = new Espresso();
// 描述和价格
System.out.println(beverage.getDescription() + " $" + beverage.cost());
Beverage beverage1 = new HouseBlend(); beverage1 = new Mocha(beverage1);
beverage1 = new Whip(beverage1);
beverage1 = new Soy(beverage1);
System.out.println(beverage1.getDescription() + " $" + beverage1.cost());
Beverage beverage2 = new Espresso(); beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
beverage2 = new Soy(beverage2);
beverage2 = new Mocha(beverage2);
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
} }
运行结果:

装饰者和被装饰者具有共同的超类,利用继承达到“类型匹配”,而不是利用继承获得“行为”;将装饰者和被装饰者组合时,加入新的行为。
解决本文中饮料的具体问题时,图中Component即为Beverage(可以是抽象类或者接口),而ConcreteComponent为各种饮料,Decorator(抽象装饰者)为调料的抽象类或接口,ConcreteDecoratorX则为各种具体的调料。
因为使用对象组合,可以把饮料和调料更有弹性地加以混合与匹配。
代码外部细节:
代码中实现的时候,通过构造函数将被装饰者传入装饰者中即可,如最后的调用形式如下:
Beverage beverage = new DarkRoast();
beverage = new Mocha(beverage);
beverage = new Whip(beverage);
即完成了两层包装,此时再调用beverage的cost()函数即可得到总价。
七、java.io包内的装饰者模式

装饰者模式的缺点:在设计中加入大量的小类,如果过度使用,会让程序变得复杂。
参考:http://www.cnblogs.com/mengdd/archive/2013/01/03/2843439.html
Java设计模式の装饰者模式的更多相关文章
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- 从源码角度理解Java设计模式——装饰者模式
一.饰器者模式介绍 装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活. 适用场景:动态的给一个对象添加或者撤销功能. 优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个 ...
- 【设计模式】Java设计模式 - 装饰者模式
Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...
- JAVA 设计模式 装饰者模式
用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式. 结构
- Java 设计模式—装饰者模式
在Java编程语言中,嵌套了非常多设计模式的思想,比如IO流中的缓冲流就使用到以下要介绍的装饰者设计模式. 演示样例代码: * 抽象构件角色:定义一个抽象接口,来规范准备附加功能的类 * @autho ...
- Java设计模式--装饰器模式到Java IO 流
装饰器模式 抽象构件角色:给出一个抽象接口,以规范准备接受附加责任的对象. 具体构件角色:定义准备接受附加责任的对象. 抽象装饰角色:持有一个构件对象的实例,并对应一个与抽象构件接口一致的接口. 具体 ...
- JAVA设计模式---装饰者模式
写在前面的话: 该模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案.装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的 ...
- 设计模式-装饰者模式(Decorator Pattern)
本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...
随机推荐
- spring boot 中文乱码问题
在刚接触spring boot 2.0的时候,遇到了一些中文乱码的问题,网上找了一些解决方法. 这里自己做个汇总. 在application.properties文件中添加: spring.http. ...
- POJ 2287 田忌赛马 贪心算法
田忌赛马,大致题意是田忌和国王赛马,赢一局得200元,输一局输掉200元,平局则财产不动. 先输入一个整数N,接下来一行是田忌的N匹马,下一行是国王的N匹马.当N为0时结束. 此题为贪心算法解答,有两 ...
- logstash+elasticsearch 错误摘记
[2017-09-17T06:00:22,511][WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [] uncaught exception ...
- BluetoothDevice详解
一. BluetoothDevice简介 1. 继承关系 public static Class BluetoothDevice extends Object implement Parcelable ...
- Alpha 冲刺4
队名:日不落战队 安琪(队长) 今天完成的任务 组织第四次站立式会议. 完成40%草稿箱前端界面. 明天的计划 剩下的60%草稿箱前端界面. 如果还有时间,尝试去调用数据. 还剩下的任务 回收站前端界 ...
- iOS- NSThread/NSOperation/GCD 三种多线程技术的对比及实现
1.iOS的三种多线程技术 1.NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) 2.以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 ...
- python 爬虫 糗百成人
import urllib from time import sleep import requests from lxml import etree try: def all_links(url,p ...
- RT thread 设备驱动组件之USART设备
本文以stm32f4xx平台介绍串口驱动,主要目的是:1.RTT中如何编写中断处理程序:2.如何编写RTT设备驱动接口代码:3.了解串行设备的常见处理机制.所涉及的主要源码文件有:驱动框架文件(usa ...
- BZOJ 1045 糖果传递(思维)
设第i个人给了第i+1个人mi个糖果(可以为负),因为最后每个人的糖果都会变成sum/n. 可以得到方程组 mi-mi+1=a[i+1]-sum/n.(1<=i<=n). 把方程组化为mn ...
- BZOJ4869 六省联考2017相逢是问候(线段树+欧拉函数)
由扩展欧拉定理,a^(a^(a^(……^x)))%p中x作为指数的模数应该是φ(φ(φ(φ(……p)))),而p取log次φ就会变为1,也即每个位置一旦被修改一定次数后就会变为定值.线段树维护区间剩余 ...