转载自 :  http://blog.csdn.net/chenyujing1234

例子代码:(编译工具:Eclipse)

http://www.rayfile.com/zh-cn/files/1291b5bd-9418-11e1-b6a1-0015c55db73d/

参考书籍: <<软件秘笈-----设计模式那点事>>

装饰者模式(Decorator ['dekəreitə]  Pattren),是在不改变原类文件和使用继承的情况下,动态地扩展一个对象的功能,它是通过创建一个包装对象,也就是

装饰来包裹真实的对象。

使用装饰者模式的时候需要注意以下几点内容:
(1)装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
(2)装饰对象包含一个真实对象的引用。
(3)装饰对象接受所有来自客户端的请求,并把这些请求转发给真实的对象。
(4)装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定的对象的结构就可以在外部增加附加功能。
在面向对象的设计中,通常是通过继承来实现对给定的类的功能扩展,然而,装饰者模式不需要子类,可以在应用程序运行时动态扩展功能。

1、一般化分析

分析一下染色馒头的制作过程:
(1)需要生产一个正常馒头;
(2)为了节省成本(不使用玉米面),使用染色剂加入到正常馒头中;
(3)通过和面机搅拌,最后生产出“玉米馒头”。
[java] view plaincopy
  1. /**
  2. * 馒头加工接口
  3. *
  4. * @author
  5. *
  6. */
  7. public interface IBread {
  8. // 准备材料
  9. public void prepair();
  10. // 和面
  11. public void kneadFlour();
  12. // 蒸馒头
  13. public void steamed();
  14. /**
  15. * 加工馒头方法
  16. */
  17. public void process();
  18. }
[java] view plaincopy
  1. /**
  2. * 正常馒头的实现
  3. *
  4. * @author
  5. *
  6. */
  7. public class NormalBread implements IBread {
  8. // 准备材料
  9. public void prepair() {
  10. System.out.println("准备面粉、水以及发酵粉...");
  11. }
  12. // 和面
  13. public void kneadFlour() {
  14. System.out.println("和面...");
  15. }
  16. // 蒸馒头
  17. public void steamed() {
  18. System.out.println("蒸馒头...香喷喷的馒头出炉了!");
  19. }
  20. /**
  21. * 加工馒头方法
  22. */
  23. public void process() {
  24. // 准备材料
  25. prepair();
  26. // 和面
  27. kneadFlour();
  28. // 蒸馒头
  29. steamed();
  30. }
  31. }
[java] view plaincopy
  1. /**
  2. * 染色的玉米馒头
  3. *
  4. * @author
  5. *
  6. */
  7. public class CornBread extends NormalBread {
  8. // 黑心商贩 开始染色了
  9. public void paint() {
  10. System.out.println("添加柠檬黄的着色剂...");
  11. }
  12. // 重载父类的和面方法
  13. @Override
  14. public void kneadFlour() {
  15. // 在面粉中加入 染色剂 之后才开始和面
  16. this.paint();
  17. // 和面
  18. super.kneadFlour();
  19. }
  20. }

[java] view plaincopy
  1. /**
  2. * 甜蜜素馒头
  3. *
  4. * @author
  5. *
  6. */
  7. public class SweetBread extends NormalBread {
  8. // 黑心商贩 开始添加甜蜜素
  9. public void paint() {
  10. System.out.println("添加甜蜜素...");
  11. }
  12. // 重载父类的和面方法
  13. @Override
  14. public void kneadFlour() {
  15. // 在面粉中加入 甜蜜素 之后才开始和面
  16. this.paint();
  17. // 和面
  18. super.kneadFlour();
  19. }
  20. }
我们知道,馒头的种类是很多的,现在黑心商贩又想生产“甜玉米馒头"了,怎么办叱?你可能要说“使用继承”不就行了吗?
再创建一个馒头,继承正常馒头类。不可否认,这样做是不错的,可以实现要求。然而,反映到类图上是又多了一个子类,如果还有其他的馒头种类,
都要继承吗?那样的话类就要爆炸了,庞大的继承类关系图。
继承方式存在这样两点不利因素:
(1)父类的依赖程序过高,父类修改会影响到子类的行为。
(2)不能复用已有的类,造成子类过多。

2、下面我们使用装饰者模式重新实现染色馒头的实例!

使用装饰者的静态类图,结构如图所示。
(1)为了装饰正常馒头NormalBread,我们需要一个正常馒头一样的抽象装饰者:AbstractBread,
该类和正常馒头类NormalBread一样实现IBread馒头接口,不同的是该抽象类含有一个IBread接口类型的私有属性bread,
然后通过构造方法,将外部IBread接口类型对象传入。
[java] view plaincopy
  1. /**
  2. * 抽象装饰者
  3. *
  4. * @author
  5. *
  6. */
  7. public abstract class AbstractBread implements IBread {
  8. // 存储传入的IBread对象
  9. private final IBread bread;
  10. public AbstractBread(IBread bread) {
  11. this.bread = bread;
  12. }
  13. // 准备材料
  14. public void prepair() {
  15. this.bread.prepair();
  16. }
  17. // 和面
  18. public void kneadFlour() {
  19. this.bread.kneadFlour();
  20. }
  21. // 蒸馒头
  22. public void steamed() {
  23. this.bread.steamed();
  24. }
  25. // 加工馒头方法
  26. public void process() {
  27. prepair();
  28. kneadFlour();
  29. steamed();
  30. }
  31. }

AbstractBread类满足了装饰者的要求:和真实对象具有相同的接口;包含一个真实对象的引用;接受所有来自客户端的请求,并反这些请求转发给真实的对象;
可以增加一些附加功能。
(2)创建装饰者。
A、创建染色剂装饰者----CornDecorator.
创建染色装饰者"CornDecorator",继承AbstractBread, 含有修改行为:添加柠檬黄的着色剂。
[java] view plaincopy
  1. /**
  2. * 染色的玉米馒头
  3. *
  4. * @author
  5. *
  6. */
  7. public class CornDecorator extends AbstractBread {
  8. // 构造方法
  9. public CornDecorator(IBread bread) {
  10. super(bread);
  11. }
  12. // 黑心商贩 开始染色了
  13. public void paint() {
  14. System.out.println("添加柠檬黄的着色剂...");
  15. }
  16. // 重载父类的和面方法
  17. @Override
  18. public void kneadFlour() {
  19. // 在面粉中加入 染色剂 之后才开始和面
  20. this.paint();
  21. // 和面
  22. super.kneadFlour();
  23. }
  24. }

这和上面提到的CornBread类的内容是一样的,只是多了一个构造方法。

B、创建甜蜜互装饰者-----SweetDecorator
[java] view plaincopy
  1. /**
  2. * 甜蜜素馒头
  3. *
  4. * @author
  5. *
  6. */
  7. public class SweetDecorator extends AbstractBread {
  8. // 构造方法
  9. public SweetDecorator(IBread bread) {
  10. super(bread);
  11. }
  12. // 黑心商贩 开始添加甜蜜素
  13. public void paint() {
  14. System.out.println("添加甜蜜素...");
  15. }
  16. // 重载父类的和面方法
  17. @Override
  18. public void kneadFlour() {
  19. // 在面粉中加入 甜蜜素 之后才开始和面
  20. this.paint();
  21. // 和面
  22. super.kneadFlour();
  23. }
  24. }

C、生产甜玉米馒头。

首先创建一个正常馒头,然后使用甜蜜素装饰馒头,之后再用柠檬黄的着色剂装饰馒头,最后加工馒头。
 
[java] view plaincopy
  1. /**
  2. * 客户端应用程序
  3. *
  4. * @author
  5. *
  6. */
  7. public class Client {
  8. /**
  9. * @param args
  10. */
  11. public static void main(String[] args) {
  12. // 生产装饰馒头
  13. System.out.println("\n====开始装饰馒头!!!");
  14. // 创建普通的正常馒头实例
  15. // 这是我们需要包装(装饰)的对象实例
  16. IBread normalBread = new NormalBread();
  17. // 下面就开始 对正常馒头进行装饰了!!!
  18. // 使用甜蜜素装饰馒头
  19. normalBread = new SweetDecorator(normalBread);
  20. // 使用柠檬黄的着色剂装饰馒头
  21. normalBread = new CornDecorator(normalBread);
  22. // 生产馒头信息
  23. normalBread.process();
  24. System.out.println("====装饰馒头结束!!!");
  25. }
  26. }

运行结果:
[plain] view plaincopy
  1. ====开始装饰馒头!!!
  2. 准备面粉、水以及发酵粉...
  3. 添加柠檬黄的着色剂...
  4. 添加甜蜜素...
  5. 和面...
  6. 蒸馒头...香喷喷的馒头出炉了!
  7. ====装饰馒头结束!!!
可能你会感觉比较奇怪,我们先使用的是甜蜜素,然后使用的是着色剂,应该先打印甜蜜素,然后再打印着色剂才对?
不用奇怪,这也不矛盾,因为装饰者相当于对原有对象的包装,这就像一个礼品盒,最里面是一个最普通的纸盒,
然后用一般的纸包装起来,最后使用比较漂亮的包装纸包装,我们最先看到的是最外漂亮的包装纸,其次才是里面的一般包装纸。
 

3、设计原则

(1)封装变化部分

设计模式是封装变化的最好阐释,无论哪一种设计模式针对的都是软件中存在的“变化”部分,然后

用抽象对这些“变化”的部分进行封装。使用抽象的好处在于为软件的扩展提供了很大的方便性。

在装饰者模式中合理地利用了类继承和组合的方式,非常灵活地表达了对象之间的依赖关系。

装饰者模式应用中“变化”的部分是组件的扩展功能,装饰者和被装饰者完全隔离开来,这样我们就可以任意地改变装饰者和被装饰者。

(2)"开-闭"原则

我们在需要对组件进行扩展、增添新的功能行为时,只需要实现一个特定的装饰者即可,这完全是增量修改,

对原有软件功能结构没有影响,对客户端APP来说也是完全透明的,不必关心内部实现细节。

(3)面向抽象编程,不要面向实现编程

在装饰者模式中,装饰者角色就是抽象类实现,面向抽象编程的好处就在于起到了很好的接口隔离作用。在运用时,

我们具体操作的也是抽象类引用,这些显示了面向抽象编程。

(4)优先使用对象组合,而非类继承。

装饰者模式最成功在于合理地使用了对象组合方式,通过组合灵活地扩展了组件的功能,所有的扩展功能都是通过组合而非继承获得的,

这从根本上决定了是高内聚、低耦合的。

4、使用场合

(1)当我们需要为某个现有的对象动态地增加一个新的功能或职责时,可以考虑使用装饰者模式;

(2)当某个对象的职责经常发生变化或者经常需要动态地增加职责,避免为了适应这样的变化而增加继承子类扩展的方式,因为这种方式会造成子类膨胀的速度过快,难以控制,此时可以使用装饰者模式。

客户端不会觉得对象在装饰前和装饰后有什么不同,装饰者模式可以在不创建更多子类的情况下,将对象的功能加以扩展,装饰者模式使用原来被装饰的一个子类实例,

把客户端的调用委派到装饰者。

下面我们来看一下装饰者模式的静态类图.使我们对装饰者模式有一个更加清晰的认识

A、被装饰者抽象Component:是一个接口或抽象类,是定义的核心对象。

在装饰者模式中,必然有一个被撮出来最核心、最原始的接口。这个类就是我们需要装饰类的基类。本例中是IBread接口。

B、被装饰者具体实现ConcreteComponent:这是Component类的一个实现类,我们要装饰的就是这个具体的实现类。本例中是NormalBread

C、装饰者Decorator:一般是一个抽象类。它里面有一个指向Component变量的引用。

D、装饰者实现ConcreteDecorator1和ConcreteDecorator2.

4

软件设计模式之(二)装饰者模式相关推荐

  1. 设计模式之二装饰者模式

    一 概述 1.什么是装饰者模式? 在不修改类,不使用继承的前提下,用一个对象来装饰另一个对象,以扩展目标对象的功能. 2.装饰者模式的作用: 继承也可以扩展类的功能,装饰者模式比继承更加灵活,因为继承 ...

  2. 设计模式十二之组合模式

    设计模式十二之组合模式 1. 模式的定义与特点 1.1 模式的定义 1.2 模式的特点 1.3 模式的使用场景 2. 模式的结构与实现 2.1 模式的结构 2.2 模式的实现 3. 模式在开源软件中的 ...

  3. 从王者荣耀看设计模式(七.装饰者模式)

    ##从王者荣耀看设计模式(装饰者模式) 一.简介 王者荣耀中,角色的"伤害值"和"生命值"是很重要的概念.为了保证游戏的胜利,玩家会通过在游戏开始前配置合适的游 ...

  4. 设计模式学习笔记——装饰(Decorator)模式

    设计模式学习笔记--装饰(Decorator)模式 @(设计模式)[设计模式, 装饰模式, decorator] 设计模式学习笔记装饰Decorator模式 基本介绍 装饰案例 类图 实现代码 Dis ...

  5. 探索软件设计模式(二)

    我们在前面的文章中已经介绍了一些软件的设计模式,并给出了一些非软件的例子.下面,让我们继续完成软件设计模式的探索,来看看这些模式中的行为模式及实例. 行为模式 作者总结了十一种行为模式.这些模式可以在 ...

  6. 设计模式之【装饰器模式】

    和表妹去喝奶茶 表妹:哥啊,我想喝奶茶. 我:走啊,去哪里喝? 表妹:走,我带你去,我经常去的那家,不但好喝,还可以自由搭配很多小料.我每次都是不同的搭配,换着喝,嘻嘻. 我:你倒是挺会喝的嘛~ 你看 ...

  7. IOS设计模式之二(门面模式,装饰器模式)

    本文原文请见:http://www.raywenderlich.com/46988/ios-design-patterns. 由 @krq_tiger(http://weibo.com/xmuzyq) ...

  8. C#设计模式之二十一访问者模式(Visitor Pattern)【行为型】

    一.引言 今天我们开始讲"行为型"设计模式的第九个模式,该模式是[访问者模式],英文名称是:Visitor Pattern.如果按老规矩,先从名称上来看看这个模式,我根本不能获得任 ...

  9. 《设计模式》之装饰器模式

    一.什么是装饰器模式 当需要对类的功能进行拓展时,一般可以使用继承,但如果需要拓展的功能种类很繁多,那势必会生成很多子类,增加系统的复杂性,并且使用继承实现功能拓展时,我们必须能够预见这些拓展功能,也 ...

最新文章

  1. mysql主从同步主服务器热切换
  2. php -- 检查是否存在
  3. thymeleaf模板的使用——1,thymeleaf概述|| thymeleaf 的使用方法|| 如何修改Thymeleaf的默认存放地址||Thymeleaf的相关语法
  4. Mondrian 3.14.0 服务配置说明(限功能使用验证)
  5. linux字符设备移动硬盘,Red Hat Enterprise Linux 7.5挂载NTFS移动硬盘
  6. 7号团队-团队任务3:每日例会(2018-11-29)
  7. 字体设计灵感|浓墨重彩!代表“墨”字设计案例
  8. MySQL把一个大表拆分多个表后,如何解决跨表查询效率问题
  9. Intellij Idea打包jar
  10. golang errors 取 错误 信息_golang-标准errors包的学习
  11. android 画图 工具下载,画板画图软件下载-画板画图 安卓版v1.1.0-PC6安卓网
  12. pytorch+yolov3(4)
  13. 抖音短视频运营中的六大定位法
  14. 软件需求工程五组工作日记——开篇:项目简介
  15. GK2A 火点提取运行需要要的包安装
  16. 智力题:36匹马,6条跑道,没有计时器,至少需要多少次选出最快的三匹马
  17. Android编程权威指南笔记3:Android Fragment讲解与Android Studio中的依赖关系,如何添加依赖关系
  18. JS高程 Chp.20 JSON 思维导图
  19. 安卓手机使用什么便签?
  20. nvivo怎么处理访谈记录_Java自然语言处理-第二版:书评和访谈

热门文章

  1. 一个计算机系统采用32位单字节指令,(自考02325李学干版)计算机系统结构课后习题.doc...
  2. 华为的鸿蒙系统是海思_华为继续亮剑,100倍变焦+海思麒麟1020+鸿蒙系统,这才是华为...
  3. linux远程窗口无法当机,linux – 当我已经进入远程机器时,如何回到本地?
  4. 湖南大学计算机专业硕士研究导师,湖南大学电气与信息工程学院硕士研究生导师介绍:黄文清...
  5. Android 定位c++异常
  6. html网页如何编译成hcm,HTML语言学习步骤.doc
  7. 常见国产计算机软件有哪些,8款鲜为人知的良心国产软件,实用且强大,个个堪称国货之光...
  8. js jquery 禁用a标签 点击事件
  9. 使用原生table 书写复杂人事档案表格
  10. java调用js匿名函数参数,js中匿名函数和回调函数