设计模式的征途—10.装饰(Decorator)模式
虽然目前房价依旧很高,就连我所在的成都郊区(非中心城区)的房价均价都早已破万,但却还是阻挡不了大家对新房的渴望和买房的热情。如果大家买的是清水房,那么无疑还有一项艰巨的任务在等着大家,那就是装修。对新房的装修并没有改变房屋用于居住的本质,但它可以让房子变得更加漂亮和温馨以及更加实用。在软件设计中,也有一种类似于新房装修的技术可以对已有的功能进行扩展使之更加符合用户需求,从而使得对象具有更加强大的功能,这便是本次即将介绍的装饰模式。
装饰模式(Decorator) | 学习难度:★★★☆☆ | 使用频率:★★★☆☆ |
一、图形界面构件库设计
1.1 需求背景
背景:M公司开发部基于OO技术开发了一套图形界面构件库Visual Component,该构件库提供了大量的基本构件,如窗体、文本框、列表框等等,由于在使用该构件库时,用户经常要求定制一些特殊的显示效果,例如带滚动条的窗体,带黑色边框的文本框,即带滚动条又带黑色边框的列表框等,因此经常需要对该构件库进行扩展以增强其功能,如下图所示:
如何提高图形界面构件库的可扩展性并降低其维护成本是M公司开发部的程序猿们必须要面对的一个问题。
1.2 初始设计
M公司的开发人员针对上面的需求,提出了一个基于继承复用的初始设计方案,其基本结构如下图所示:
通过分析该设计方案,不难发现存在以下问题:
(1)系统扩展麻烦,在C#/Java中根本无法实现(不支持多继承)。
(2)代码重复,不利于对系统进行修改和维护。
(3)系统庞大,类的数量非常多。
总之,这个设计不是一个好的设计方案,如何让系统利于扩展又不导致类的数量线性增加呢?让我们了解一下装饰类把。
二、装饰模式概述
2.1 装饰模式简介
装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为,在现实生活中,这种情况也到处存在,例如一张照片,可以不改变照片本身,给它增加一个相框,使得它具有防潮的功能,而且用户可以根据需要给它增加不同类型的相框,甚至可以在一个小相框的外面再套一个大相框。
装饰(Decorator)模式:动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式远比生成子类实现更加灵活。装饰模式是一种对象结构型模式
2.2 装饰模式结构
从结构图中可以看出,装饰模式主要有以下几个角色:
(1)Component (抽象构件):具体构件和抽象装饰类的基类,声明了在具体构建中实现的业务方法。
(2)ConcreteComponent(具体构件):抽象构件的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
(3)Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。
(4)ConcreteDecorator(具体装饰类):抽象装饰类的子类,负责向构件添加新的职责。
三、重构图形界面构件库
3.1 重构后的设计方案
为了让系统具有更好的灵活性和可扩展性,克服继承复用所带来的问题,M公司开发人员使用装饰模式来重构图形界面库的设计,其中部分类的基本结构如下图所示:
其中,Component充当抽象构件类,其子类Window、TextBox和ListBox充当具体构件类,ComponentDecorator则充当抽象装饰类,ScrollBarDecorator和BlackBorderDecorator则充当具体装饰类。
3.2 重构后的代码实现
(1)抽象构件:Component
/// <summary>
/// 抽象界面构件类:抽象构件类
/// </summary>
public abstract class Component
{
public abstract void Display();
}
(2)具体构件:Window, TextBox 和 ListBox
/// <summary>
/// 窗体类:具体构件类
/// </summary>
public class Window : Component
{
public override void Display()
{
Console.WriteLine("显示窗体!");
}
} /// <summary>
/// 文本框类:具体构件类
/// </summary>
public class TextBox : Component
{
public override void Display()
{
Console.WriteLine("显示文本框!");
}
} /// <summary>
/// 列表框类:具体构件类
/// </summary>
public class ListBox : Component
{
public override void Display()
{
Console.WriteLine("显示列表框!");
}
}
(3)抽象装饰:ComponentDecorator
/// <summary>
/// 构件装饰类:抽象装饰类
/// </summary>
public class ComponentDecorator : Component
{
private Component component; public ComponentDecorator (Component component)
{
this.component = component;
} public override void Display()
{
component.Display();
}
}
(4)具体装饰:ScrollBarDecorator 和 BlackBorderDecorator
/// <summary>
/// 滚动条装饰类:具体装饰类
/// </summary>
public class ScrollBarDecorator : ComponentDecorator
{
public ScrollBarDecorator(Component component) : base(component)
{ } public override void Display()
{
this.SetScrollBar();
base.Display();
} public void SetScrollBar()
{
Console.WriteLine("为构件增加滚动条!");
}
} /// <summary>
/// 黑色边框装饰类:具体装饰类
/// </summary>
public class BlackBorderDecorator : ComponentDecorator
{
public BlackBorderDecorator(Component component) : base(component)
{ } public override void Display()
{
this.SetScrollBar();
base.Display();
} public void SetScrollBar()
{
Console.WriteLine("为构件增加黑色边框!");
}
}
(5)客户端测试
public class Program
{
public static void Main(string[] args)
{
Component component = new Window();
// 一次装饰
Component componentSB = new ScrollBarDecorator(component);
componentSB.Display(); Console.WriteLine();
// 二次装饰
Component componentBB = new BlackBorderDecorator(componentSB);
componentBB.Display(); Console.ReadKey();
}
}
执行后的结果如下图所示:
可以看到,第一次装饰之后,窗体有了滚动条。第二次装饰之后,窗体不仅有了滚动条,还增加了黑色边框。
四、装饰模式小结
4.1 主要优点
(1)对于扩展一个对象的功能,装饰模式比继承更加灵活 => 不会导致类的个数急剧增加!
(2)可以对一个对象进行多次装饰,从而创造出很多不同行为的组合 => 得到功能更为强大的对象!
(3)具体构件类与具体装饰类可以独立变化,可以根据需要增加新的具体构建和具体装饰 => 原有代码无需修改,符合开放封闭原则!
4.2 主要缺点
虽然装饰模式拱了一种比继承更加灵活机动的方案,但同时也意味着比继承更加易于出错,排错也很困难。特别是经过多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
4.3 应用场景
(1)在不影响其他对象的情况下,想要动态地、透明地给单个对象添加职责 => 采用装饰模式吧!
(2)当不能采用继承的方式对系统进行扩展 或 采取继承不利于系统扩展和维护时 => 采用装饰模式吧!
参考资料
刘伟,《设计模式的艺术—软件开发人员内功修炼之道》
设计模式的征途—10.装饰(Decorator)模式的更多相关文章
- 设计模式C++描述----10.装饰(Decorator)模式
一. 举例 我之前做过一个文件系统就叫 MyFileSys 吧,后来的话,客户想加入一些附加功能,比如压缩.加密.杀毒之类的操作,这些附加操作没有先后顺序,比如你可以先压缩再加密,也可以先杀毒再压缩, ...
- 《Head First 设计模式》ch.3 装饰(Decorator)模式
设计原则 类应该对修改关闭,对扩展开放(开放-关闭原则).在每个地方使用开放-关闭原则是一种浪费,也没有必要,因为这通常会引入新的抽象层次,增加代码复杂度.需要把注意力集中在设计中最有可能改变的地方. ...
- 设计模式(八)装饰器模式Decorator(结构型)
设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法 ...
- php设计模式课程---7、装饰器模式如何使用
php设计模式课程---7.装饰器模式如何使用 一.总结 一句话总结: 装饰器的核心是获取了文章类整个类,而不是获取了文章内容,有了这个文章类,我想给你加多少装饰就给你加多少装饰(将文章这个类封装进去 ...
- 大型Java进阶专题(八)设计模式之适配器模式、装饰者模式和观察者模式
前言 今天开始我们专题的第八课了.本章节将介绍:三个设计模式,适配器模式.装饰者模式和观察者模式.通过学习适配器模式,可以优雅的解决代码功能的兼容问题.另外有重构需求的人群一定需要掌握装饰者模式. ...
- 设计模式(九)——装饰者模式(io源码分析)
1 星巴克咖啡订单项目(咖啡馆): 1) 咖啡种类/单品咖啡:Espresso(意大利浓咖啡).ShortBlack.LongBlack(美式咖啡).Decaf(无因咖啡) 2) 调料:Milk.So ...
- 装饰(Decorator)模式
1.装饰(Decorator)模式 动态给一个对象添加一些额外的职责.就增加功能来说,装饰模式比生成子类更为灵活.Component是定义一个对象接口.可以给这些对象动态地添加职责.Concre ...
- 设计模式之第10章-桥接模式(Java实现)
设计模式之第10章-桥接模式(Java实现) “一入软件深似海,从此早睡是路人.黑夜给了我黑色的眼睛,我却用他去寻找八阿哥.”“怎么了,又来那么多的感慨啊.”“还能有什么啊,老板是说让换个APP做,这 ...
- ④ 设计模式的艺术-10.装饰(Decorator)模式
职责 装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 装饰模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对 ...
随机推荐
- android中跨线程向控件传值的问题
activity.oncreate(bundle savedinstancestate)中创建一个handler类的实例, 在这个handler实例的handlemessage回调函数中调用更新界面显 ...
- 玩转INotifyPropertyChanged和ObservableCollection
转载: http://www.cnblogs.com/Jax/archive/2009/10/13/1582128.html 本文的代码都是基于WPF的,对于Silverlight,这些技术也同样 ...
- mongodb生产环境(副本集模式)集群搭建配置
mongodb副本集模式由如下几部分组成: 1.路由实例mongos 2.配置实例configsvr 3.副本集集群replset(一主多从) tips: 1.以上实例都是mongod守护进程 2.以 ...
- Python开篇
一:Python的前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为 ...
- 设置QT应用程序图标方法(Windows下)
学习笔记,言简意赅. 1- 新建文本文件,编辑输入 IDI_ICON1 ICON DISCARDABLE "./image/WindowIco.ico" 注意: ...
- LeetCode 118. Pascal's Triangle (杨辉三角)
Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Retur ...
- java时间格式
Calendar startdate = Calendar.getInstance(); startdate.setTime(new Date()); //当前时间 startdate.add(Cal ...
- 读书笔记-你不知道的JS上-混入与原型
继承 mixin混合继承 function mixin(obj1, obj2) { for (var key in obj2) { //重复不复制 if (!(key in obj1)) { obj1 ...
- PyQt5安装目录中找不到designer.exe与pyrcc5.exe
我安装的是PyQt5的5.9版本,在安装目录下找不到designer.exe文件.在摸索一段后发现5.9版本对库文件和相关的开发工具是分开发布的.QtDesigner是在pyqt5-tools的包里. ...
- rwx对于文件和目录的意义
1.对于文件 r:可读. w:可以编辑,可以修改. x:可以执行.在windows中,可执行指的是.exe,.bat等这些后缀结尾的文件,在linux没有这种限制. 2.对于目录 r:表示可以用ls命 ...