虽然目前房价依旧很高,就连我所在的成都郊区(非中心城区)的房价均价都早已破万,但却仍是阻挡不了你们对新房的渴望和买房的热情。若是你们买的是清水房,那么无疑还有一项艰巨的任务在等着你们,那就是装修。对新房的装修并无改变房屋用于居住的本质,但它可让房子变得更加漂亮和舒适以及更加实用。在软件设计中,也有一种相似于新房装修的技术能够对已有的功能进行扩展使之更加符合用户需求,从而使得对象具备更增强大的功能,这即是本次即将介绍的装饰模式。设计模式
装饰模式(Decorator) | 学习难度:★★★☆☆ | 使用频率:★★★☆☆ |
背景:M公司开发部基于OO技术开发了一套图形界面构件库Visual Component,该构件库提供了大量的基本构件,如窗体、文本框、列表框等等,因为在使用该构件库时,用户常常要求定制一些特殊的显示效果,例如带滚动条的窗体,带黑色边框的文本框,即带滚动条又带黑色边框的列表框等,所以常常须要对该构件库进行扩展以加强其功能,以下图所示:ide
如何提升图形界面构件库的可扩展性并下降其维护成本是M公司开发部的程序猿们必需要面对的一个问题。学习
M公司的开发人员针对上面的需求,提出了一个基于继承复用的初始设计方案,其基本结构以下图所示:测试
经过分析该设计方案,不难发现存在如下问题:this
(1)系统扩展麻烦,在C#/Java中根本没法实现(不支持多继承)。spa
(2)代码重复,不利于对系统进行修改和维护。设计
(3)系统庞大,类的数量很是多。调试
总之,这个设计不是一个好的设计方案,如何让系统利于扩展又不致使类的数量线性增长呢?让咱们了解一下装饰类把。code
装饰模式能够在不改变一个对象自己功能的基础上给对象增长额外的新行为,在现实生活中,这种状况也处处存在,例如一张照片,能够不改变照片自己,给它增长一个相框,使得它具备防潮的功能,并且用户能够根据须要给它增长不一样类型的相框,甚至能够在一个小相框的外面再套一个大相框。component
装饰(Decorator)模式:动态地给一个对象增长一些额外的职责,就增长对象功能来讲,装饰模式远比生成子类实现更加灵活。装饰模式是一种对象结构型模式
从结构图中能够看出,装饰模式主要有如下几个角色:
(1)Component (抽象构件):具体构件和抽象装饰类的基类,声明了在具体构建中实现的业务方法。
(2)ConcreteComponent(具体构件):抽象构件的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器能够给它增长额外的职责(方法)。
(3)Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增长职责,可是具体职责在其子类中实现。
(4)ConcreteDecorator(具体装饰类):抽象装饰类的子类,负责向构件添加新的职责。
为了让系统具备更好的灵活性和可扩展性,克服继承复用所带来的问题,M公司开发人员使用装饰模式来重构图形界面库的设计,其中部分类的基本结构以下图所示:
其中,Component充当抽象构件类,其子类Window、TextBox和ListBox充当具体构件类,ComponentDecorator则充当抽象装饰类,ScrollBarDecorator和BlackBorderDecorator则充当具体装饰类。
(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(); } }
执行后的结果以下图所示:
能够看到,第一次装饰以后,窗体有了滚动条。第二次装饰以后,窗体不只有了滚动条,还增长了黑色边框。
(1)对于扩展一个对象的功能,装饰模式比继承更加灵活 => 不会致使类的个数急剧增长!
(2)能够对一个对象进行屡次装饰,从而创造出不少不一样行为的组合 => 获得功能更为强大的对象!
(3)具体构件类与具体装饰类能够独立变化,能够根据须要增长新的具体构建和具体装饰 => 原有代码无需修改,符合开放封闭原则!
虽然装饰模式拱了一种比继承更加灵活机动的方案,但同时也意味着比继承更加易于出错,排错也很困难。特别是通过屡次装饰的对象,调试时寻找错误可能须要逐级排查,较为繁琐。
(1)在不影响其余对象的状况下,想要动态地、透明地给单个对象添加职责 => 采用装饰模式吧!
(2)当不能采用继承的方式对系统进行扩展 或 采起继承不利于系统扩展和维护时 => 采用装饰模式吧!
刘伟,《设计模式的艺术—软件开发人员内功修炼之道》