设计模式的艺术之道–装饰模式

声明:本系列为刘伟老师博客内容总结(http://blog.csdn.net/lovelion),博客中有完整的设计模式的相关博文,以及作者的出版书籍推荐

本系列内容思路分析借鉴了刘伟老师的博文内容,同时改用C#代码进行代码的演示和分析(Java资料过多 C#表示默哀).

本系列全部源码均在文末地址给出。


本系列开始讲解结构型模式,关注如何将现有类或对象组织在一起形成更加强大的结构。
不同的结构型模式从不同的角度组合类或对象,它们在尽可能满足各种面向对象设计原则的同时为类或对象的组合提供一系列巧妙的解决方案。

  • 类结构型模式
    关心类的组合,由多个类组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系
  • 对象结构型模式
    关心类与对象的组合,通过关联关系,在一个类中定义另一个类的实例对象,然后通过该对象调用相应的方法
    7种常见的结构型模式

装饰模式

独自北漂的小王,生活拮据,只能住得起毛坯房。但是小王也是个有生活追求的人。对出租房进行了简单的装修工作,装修可以让小王住的更加舒服。在软件设计中,我们也有一种类似新房装修的技术可以对已有对象(新房)的功能进行扩展(装修),以获得更加符合用户需求的对象,使得对象具有更加强大的功能。

1.1定义

  • 装饰模式 (Decorator Pattern):动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。
  • 以对客户透明的方式动态地给一个对象附加上更多的责任
  • 可以在不需要创建更多子类的情况下,让对象的功能得以扩展

1.2情景实例

问题描述
- 图形界面构件库的设计
菜鸟软件公司基于面向对象技术开发了一套图形界面构件库VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等,由于在使用该构件库时,用户经常要求定制一些特效显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能

初步思路
一个基于继承复用的初始设计方案,在抽象类Component中声明了抽象方法display(),其子类Window、TextBox等实现了display()方法,可以显示最简单的控件,再通过它们的子类来对功能进行扩展,例如,在Window的子类ScrollBarWindow、BlackBorderWindow中对Window中的display()方法进行扩展,分别实现带滚动条和带黑色边框的窗体。
UML类图

现有缺点(未来变化)
(1) 系统扩展麻烦,在某些编程语言中无法实现。有多继承(C# Java不支持)
(2)代码重复。如滚动条设置方法,重复太多。
(3) 系统庞大,类的数目非常多。如果增加新的控件需要增加大量的具体类,这将导致系统变得非常庞大
如何改进
如何让系统中的类可以进行扩展但是又不会导致类数目的急剧增加?不用着急,让我们先来分析为什么这个设计方案会存在如此多的问题。根本原因在于复用机制的不合理。
根据“合成复用原则”,在实现功能复用时,我们要多用关联,少用继承。
改进UML类图

实例关键代码

namespace DecoratorSample
{abstract class VisualComponent{//抽象的组件public abstract void Display();}//其他类似省略class ListBox : VisualComponent{public override void Display(){Console.WriteLine("显示列表框!");}}class ComponentDecorator : VisualComponent{private VisualComponent component;  //维持对抽象构件类型对象的引用//注入抽象构件类型的对象public ComponentDecorator(VisualComponent component){this.component = component;}public override void Display(){component.Display();}}//其他类似省略class ScrollBarDecorator : ComponentDecorator{public ScrollBarDecorator(VisualComponent component): base(component){}public override void Display(){this.SetScrollBar();base.Display();}public void SetScrollBar() {Console.WriteLine("为构件增加滚动条!");}}class Program{static void Main(string[] args){VisualComponent component, componentSB, componentBB; //使用抽象构件定义component = new Window(); //定义具体构件componentSB = new ScrollBarDecorator(component); //定义装饰后的构件componentBB = new BlackBorderDecorator(componentSB); //将装饰了一次之后的对象继续注入到另一个装饰类中,进行第二次装饰componentBB.Display();Console.Read();}}
}

1.3模式分析

动机和意图

  • 如何在不改变一个对象本身功能的基础上给对象增加额外的新行为?

一般结构

  • 装饰模式包含4个角色:
  • Component(抽象构件):它是具体构件和抽象装饰类的共同父类,包含共有的业务方法
  • ConcreteComponent(具体构件):用于定义具体的构件对象,实现抽象构件中声明的方法
  • Decorator(抽象装饰类):用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用。
  • ConcreteDecorator(具体装饰类):负责向构件添加新的职责。

    装饰模式UML类图

改进后的优点

  • 对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加

  • 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类

  • 可以对一个对象进行多次装饰
  • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类

现存的缺点

  • 使用装饰模式进行系统设计时将产生很多小对象,在一定程度上影响程序的性能
  • 比继承更加易于出错,排错也更困难

优化空间
如果客户端希望单独调用具体装饰类新增的方法,而不想通过抽象构件中声明的方法来调用新增方法时将遇到一些麻烦?
透明装饰模式的设计难度较大(上边的就是透明模式),而且有时我们需要单独调用新增的业务方法。为了能够调用到新增方法,我们不得不用具体装饰类型来定义装饰之后的对象,而具体构件类型还是可以使用抽象构件类型来定义,这种装饰模式即为半透明装饰模式,也就是说,对于客户端而言,具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的。
举例菜鸟软件公司开发的OA系统中,采购单(PurchaseRequest)和请假条(LeaveRequest)等文件(Document)对象都具有显示功能,现在要为其增加审批、删除等功能,使用装饰模式进行设计。

Approver类继承了抽象装饰类Decorator的display()方法,同时新增了业务方法approve(),但这两个方法是独立的,没有任何调用关系。如果客户端需要分别调用这两个方法。

Document  doc; //使用抽象构件类型定义
doc = new PurchaseRequest();
Approver newDoc; //使用具体装饰类型定义
newDoc = new Approver(doc);
newDoc.display();//调用原有业务方法
newDoc.approve();//调用新增业务方法

完整代码部分见下载源码。

注意事项
(1) 尽量保持装饰类的接口与被装饰类的接口相同,尽量使用透明模式
(2) 尽量保持具体构件类ConcreteComponent是一个“轻”类,也就是说不要把太多的行为放在具体构件类中,我们可以通过装饰类对其进行扩展。
(3) 如果只有一个具体构件类,那么抽象装饰类可以作为该具体构件类的直接子类。

适用场景
(1) 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
(2)当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时

举例:Word字体的装饰功能 字体本身是为了显示 但是可以变成新的颜色 变成新的字体 新的字号

实例源代码
GitHub地址
百度云地址:链接: https://pan.baidu.com/s/1skItmUh 密码: 7igv

设计模式的艺术之道--装饰模式相关推荐

  1. 设计模式的艺术——软件开发人员内功修炼之道 重磅来袭

    今天(2012年12月17日),拿到了清华大学出版社给我寄的<设计模式的艺术--软件开发人员内功修炼之道>样书,这本近400页的书凝聚了过去多年我对设计模式的实战经验和教学精华,感谢清华大 ...

  2. 《设计模式的艺术——软件开发人员内功修炼之道》重磅来袭!

    今天(2012年12月17日),拿到了清华大学出版社给我寄的<设计模式的艺术--软件开发人员内功修炼之道>样书,这本近400页的书凝聚了过去多年我对设计模式的实战经验和教学精华,感谢清华大 ...

  3. 设计模式的艺术——软件开发人员内功修炼之道 交流贴

     <设计模式的艺术--软件开发人员内功修炼之道>一书正式出版发行,已在国内各大知名电子商务网站陆续上架. 当当网            亚马逊            京东网 在本书的作者简 ...

  4. 《设计模式的艺术——软件开发人员内功修炼之道》交流贴

     <设计模式的艺术--软件开发人员内功修炼之道>一书正式出版发行,已在国内各大知名电子商务网站陆续上架. 当当网            亚马逊            京东网 在本书的作者简 ...

  5. 《设计模式的艺术》读书笔记

    文章目录 预备知识:UML类图 类与类之间的关系 1. 关联关系 (1) 双向关联 (2) 单向关联 (3) 自关联 (4) 多重性关联 (5) 聚合关系 (6) 组合关系 2. 依赖关系 3. 泛化 ...

  6. 设计模式(13):结构型-装饰模式(Decorator)

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...

  7. 用心之作,倾心出品——《设计模式的艺术》震撼来袭!

    用心之作,倾心出品!<设计模式的艺术>双色版正式出版,再次感谢清华大学出版社! 一本修炼编程内功的设计模式著作,内容涵盖本博客所有精品文章! 图书目录可参考: 史上最全设计模式导学目录(完 ...

  8. java桥接和装饰_设计模式:桥接模式和装饰模式

    原标题:设计模式:桥接模式和装饰模式 一.桥接模式简介 1.基础描述 桥梁模式是对象的结构模式.又称为柄体(Handle and Body)模式或接口(Interface)模式.桥梁模式的用意是&qu ...

  9. c语言 适配器模式例子,NodeJS设计模式总结【单例模式,适配器模式,装饰模式,观察者模式】...

    NodeJS设计模式总结[单例模式,适配器模式,装饰模式,观察者模式] 发布时间:2020-08-21 03:08:03 来源:脚本之家 阅读:117 作者:lucky芬 本文实例讲述了NodeJS设 ...

最新文章

  1. 文件错误关于hibernate中报Duplicate class/entity mapping org.model.User错的问题
  2. The DotNet Garbage Collection
  3. 实战Javascript:结合电商主界面实现轮播图和倒计时秒杀
  4. Facebook股价周四大涨15.5% 市值超亚马逊
  5. 框架应该弄明白的理论问题
  6. Java面试:mysql批量更新
  7. opencv图像灰化_Opencv——彩色图像灰度化的三种算法
  8. mac安静执行脚本_自动切换mac输入法-安静模式
  9. Win 10系统截图的7种方式【简单实用】
  10. acm会议什么档次_国际顶级会议期刊级别介绍
  11. 小白也能懂的DPDK技术解析
  12. Kubernetes(k8s)的Pod资源清单spec.containers属性详细讲解
  13. 南京信息工程大学计算机考研怎么样,南京信息工程大学就业怎么样,考研好不好?...
  14. 4595: [Shoi2015]激光发生器
  15. 游戏建模大神教你三招学会用maya制作三维动画特效
  16. unreal 渲染讲的比较好的材料
  17. 快速排序算法(图解+代码)
  18. php线程教程,实例讲解php实现多线程
  19. 【WORD】单词分栏
  20. 加拿大安省哪个高中注重计算机,安省高中体制,你真的了解吗

热门文章

  1. Hadoop HDFS原理笔记
  2. MepReduce-开启大数据计算之门
  3. 使用cv2进行图像融合
  4. python学习笔记之小小购物车
  5. c++实现AES加密解密算法
  6. 台式电脑一般价钱多少_台式电脑什么配置好一点(一般价位大概多少钱)
  7. iPhone相机和内存警告
  8. 双亲委派模型为什么要打破双亲委派模型
  9. 用 Java 撸一个身份证号码识别系统,准确率高达 90%
  10. 安防民用市场的“小确进”