1 | 装饰模式的概述

我们在了解装饰模式之前,先回顾下生活中的几个常见现象,举例如下:

  • 新房的装修,房屋装修并没有改变房屋居住的本质,但可以让房屋变得更漂亮,更温馨,更实用,更满足居家需求。
  • 相片的包装,照相馆中把原相片清洗出来后,会对上面做些包装/装饰,相片镀膜,添加相框等处理,让整体更加美观,防潮保存更长的时间。

在软件设计中,类似上面的场景我们也可以把对象在不改变结构的情况下对其加工扩展修饰,使得对象具有更加强大的功能,这种技术在设计模式中就叫装饰模式。装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为。

1.1 装饰模式的定义

  • 装饰模式:动态地给一个对象增加一些额外的职责。就扩展功能面言,装饰模式提供了—种比使用子类更加灵活的替代方案。
  • Decorator Pattern:Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

装饰模式是一种对象结构型模式,它以对客户透明的方式动态地给一个对象附加上更多的责任,可以在不需要创建更多子类的情况下,让对象的功能得以扩展。

装饰模式是一种用于替代继承的技术,它通过一开无须定义子类的方式给对象动态增加职责,使用对象之间的关联天系取代类之间的继承关系。装饰模式降低了系统的耦合度。可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和用于装饰的具体装饰类都可以独立变化,增加新的具体构件类和具体装饰类都非常方便,符合开闭原则。

2 | 装饰模式的结构与实现

2.1 装饰模式的结构

类图结构

装饰模式包含以下 4 个角色

  • (1) Componcnt(抽象构件),它是具体构件和抽象装饰类的共同父奥,声明了在具体构件中实轰的业备方法,它的引入可以使客户湖以一致的方式处理未被装饰的对象以及装饰之后的对象,字现客户端的透明操作。
  • (2) ConcreteComponent(具体钩件),它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象物件中声明的方法,装饰类可以给它增加额外的职责(方法)。
  • (3) Decorater(抽象装饰类),它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责通常在其子类中实舰,它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子面扩展该方法,以达到装饰的目的。
  • (4) ConcreteDecorator(具体装饰类),它是抽象装饰类的子类,负责向构件添加新的职责,每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并且可以增加新的方法,以实现扩展对象的行为。

2.2 装饰模式的实现

【抽象构件类/Component】一般设计为抽象类或者接口,在其中声明了抽象业务方法,当然,也可以在抽象构件类中实现一些所有具体构件类都共有的业务方法。抽象构件类的典型代码如下:

abstract class Component
{public abstract void Operation();
}

【具体构件类/ConcreteComponent 】作为抽象构件类的子类实现了在抽象构件类中声明的业务方法,通常在具体构件类中只提供基本功能的实现,一些复杂的功能需通过装饰类来进行扩展,其典型代码如下:

class ConcreteComponent : Component
{public override void Operation(){Console.WriteLine("基本功能实现");}
}

【抽象装饰类/Decorator 】装饰模式的核心在于装饰类的设计,其典型代码如下:

class Decorator : Component
{private readonly Component _Component;  //维持一个对抽象构件对象的引用//注入一个抽象构件类型的对象public Decorator(Component component){_Component = component;}public override void Operation(){_Component.Operation();  //调用原有业务方法}
}

【具体装饰类/ConcreteDecorator】是抽象装饰类的子类,可以根据需要对父类方法 Operation() 进行扩展,典型代码如下:

class ConcreteDecorator : Decorator
{public ConcreteDecorator(Component component) : base(component){}public override void Operation() {base.Operation();  //调用原有业务方法AddedBehavior();  //调用新增业务方法}//新增业务方法public void AddedBehavior() { Console.WriteLine("功能扩展实现");}
}

在具体装饰类中可以调用抽象装饰类的 Operation() 方法,同时可以定义新的业务方法,例如 AddedBehavior() ,如果该方法不希望客户端单独调用,还可以将其可访问性修改为私有(private)。

客户端调用

3 | 装饰模式的应用实例

3.1 实例说明

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

3.2 实例代码设计

类图结构

示例代码说明

  • VisualComponent :抽象界面构件类,充当抽象构建类。
  • Window、TextBox、ListBox :VisualComponent 的派生类,充当具体构建类。
  • ComponentDecorator :VisualComponent 的派生类,充当抽象装饰器类。
  • BlackBorderDecorator、ScrollBarDecorator :ComponentDecorator 的派生类,充当具体装饰类。

客户端调用

如果需要在原系统中增加一个新的具体构建类或者新的具体装饰类,无须修改现有的类库代码,只需将他们分别作为抽象构建类或者抽象装饰类的子类即可。

完整代码示例请查看 =》https://gitee.com/dolayout/DesignPatternOfCSharp/tree/master/DesignPatternOfCSharp/DecoratorPattern

4 | 透明装饰模式与半透明装饰模式

在装饰模式中,具体装饰类通过新增成员变量或者成员方法来扩展具体构建类的功能。

  • 在标准的装饰模式中,新增行为需要在原有的业务方法中调用,无论是具体构建对象还是装饰过后的构建对象,对于客户端而言都是透明的,这种装饰模式被称为透明(Transparent)装饰模式
  • 但是在某些情况下,有些新增行为可能需要单独被调用,此时,客户端不能再一致性地处理装饰之前的对象和装饰之后的对象,这种装饰模式被称为半透明(Semi-transparent)装饰模式

下面将对这两种装饰模式进行较为详细的介绍。

4.1 透明装饰模式

在透明装饰模式中,要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。对于客户端而言,具体构件对象和具体装饰对象没有任何区别。即应该使用以下代码:

Component component = new ConcreteComponent(); //推荐:使用抽象构件类型定义对象
//ConcreteComponent component = new ConcreteComponent(); //不推荐:使用具体构件类型定义对象Decorator decorator = new ConcreteDecorator(component); //推荐:抽象装饰器类型定义对象
//ConcreteDecorator decorator = new ConcreteDecorator(component); //不推荐:具体装饰器类型定义对象

对于多次装饰而言,在客户端中存在以下代码片段:

Component component_o, component_d1, component_d2; //全部使用抽象构件定义
component_o = new ConcreteComponent();
component_d1 = new ConcreteDecoratoz1(component_o);
component_d2 = new ConcreteDecorator2(component_d1);
component_d2.Operation();
//无法单独调用 component_d2 的 AddedBehavior() 方法

使用抽象构件类型 Component 定义全部具体构件对象和具体装饰对象,客户端可以一致地使用这此对象,因此符合透明装饰模式的要求。
透明装饰模式可以让客户端透明地使用装饰之前的对象和装饰之后的对象,无须关心它们的区别,此外,还可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象,在实现透明装饰模式时,要求具体装饰类的 Operation() 方法覆盖抽象装饰类的 Operation() 方法,除了调用原有对象的 Operation() 外还需要调用新增的 AddedBehavior() 方法来增加新行为。但是由于在抽象构件中并没有声明 AddedBehavior() 方法,因此,无法在客户端单独调用该方法,上面的图形界面构件库的设计方案中使用的就是透明装饰模式。

4.2 半透明装饰模式

透明装饰模式的设计难度较大,而且有时需要单独调用新增的业务方法,为了能够调用到新增的方法,不得不用具体的装饰类来定义装饰后的对象,而具体构件可以继续使用抽象对象构件类型来定义,这种装饰模式即为半透明装饰模式。

客户端调用

  1. 具体构件类型无须关心,是透明的,使用抽象构建类型定义;
  2. 具体装饰类型必须指定,是不透明的,使用具体装饰类型定义;

客户端示例代码片段如下:

Component component = new ConcreteComponent(); //使用抽象构建类型定义
ConcreteDecorator decorator = new ConcreteDecorator(component); //使用具体装饰类型定义
decorator.Operation(); //调用单独的新增业务方法

半透明装饰模式可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便;但是其最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象。在实现半透明的装饰模式时,只需在具体装饰类中增加一个独立的 AddedBehavior() 方法来封装相应的业务处理即可,由于客户端使用具体装饰类型来定义装饰后的对象,因此可以单独调用 AddedBehavior() 方法。

5 | 装饰模式的优缺点与适用环境

装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和用于装饰的具体装饰类可以独立变化,以便增加新的且休构件米和且体装饰类。使用装饰模式将大大减少子类的个数,让系统扩展起来更加方俑,而日更灾具维护,是取代继承复用的有效方式之一。在软件开发中,装饰模式得到了较为广泛的应用。

5.1 装饰模式的优点

  • (1)对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加。
  • (2)装饰模式可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
  • (3)装饰模式可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
  • (4)在装饰模式中,具体构件类与具体装饰类是可以独立变化的,用户可以根据需要增加新的具体构件类和具体装饰类,且原有类库代码无须改变,符合开闭原则。

5.2 装饰模式的缺点

  • (1)使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能。
  • (2)装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也更困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

5.3 装饰模式的适用环境

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

不能采用继承的情况主要有两种:

  1. 第一种是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;
  2. 第二种是因为类已定义为不能被继承(例如 C# 语言中的密封类,即使用 sealed 关键字修饰的类);

设计模式 | 装饰模式相关推荐

  1. 设计模式----装饰模式

    设计模式--装饰模式 "装饰模式(Decorator)"又名"包装模式(Wrapper)",通常用来灵活地扩充对象的功能. 在此之前我们可以通过类的继承来扩充父 ...

  2. 大话设计模式-装饰模式(大鸟和小菜Java版)

    装饰模式:装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象.(百度百科) 这个模式让后期的修改变得极为简单,真的就高内 ...

  3. 大话设计模式—装饰模式

    装饰模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装原 ...

  4. 设计模式--装饰模式

    目录 什么是装饰模式? 应用代码示例 装饰模式模板 jdk中的装饰模式 为什么装饰器类不能直接实现Component父类? 什么是装饰模式? 以生活中的场景来举例,一个蛋糕胚,给它涂上奶油就变成了奶油 ...

  5. C++设计模式-装饰模式

    目录 基本概念 代码和实例 基本概念 装饰模式是为已有功能动态地添加更多功能的一种方式. 当系统需要新功能的时候,是向旧系统的类中添加新代码.这些新代码通常装饰了原有类的核心职责或主要行为. 装饰模式 ...

  6. 李建忠设计模式——装饰模式

    1."单一职责"模式 在软件组件的设计中,如果责任划分不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任. 典型模式 Deco ...

  7. C++设计模式——装饰模式(高屋建瓴)

    原网址:https://blog.csdn.net/CoderAldrich/article/details/83115394 重点在于 ConcreteDecoratorA(Component *d ...

  8. java设计模式——装饰模式

    装饰模式也称为包装模式.结构型设计模式之一,其使用一种对客户端透明的方式动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一. 装饰模式可以动态的给一个对象添加一些额外的职责.就增加功能功能来说 ...

  9. 设计模式——装饰模式详解

    0. 前言   写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇 ...

最新文章

  1. LeetCode简单题之Excel 表中某个范围内的单元格
  2. 如何使用Arthas定位线上 Dubbo 线程池满异常
  3. C语言程序的基本结构
  4. html js css如何关联_html+css +js 选项卡
  5. ORACLE1.10 - 一对多
  6. python 输入密码加密显示_如何在python中用密码加密文本?
  7. oracle报27040错误,【oracle案例】创建表空间时遇到 ORA-01119,0RA-27040,0SD-04002
  8. element ui 邮箱非必填校验
  9. 拓展卡尔曼滤波器(EKF)的数学推导
  10. 苹果电脑删除软件_5款Mac查杀恶意流氓软件,防护你的MacOS电脑,随时清理优化更加安全!...
  11. oracle union orderby,Oracle UNION和ORDER BY的奇怪问题
  12. Node.js格式化输出json文件
  13. CMM(Capability Maturity Model) 能力成熟度模型
  14. 反燃油车占位方案:AI识别+EasyCVR解决燃油车占位问题
  15. 读内存为什么比读取磁盘快?快多少?
  16. 手机终于可以看到正在上映的院线电影了
  17. uni-app中使用uniCloud实现发送短信验证码(开通、配置、使用)
  18. 蓝桥杯真题 ——单词分析(python3)
  19. 洛谷 P3797 妖梦斩木棒 解题报告
  20. python实现将数据写入Excel文件中

热门文章

  1. 瑶琳c语言,中国浙江省的瑶琳仙境属于 A.旅游资源中的地质地貌景观 B.旅游资源中水文地理景观 C.旅游资源中生物景观 D.旅游资源中文化艺术景观...
  2. 华为鸿蒙糸统其它手机可以用吗,鸿蒙os来了,但是除了华为以外其他手机能用吗?...
  3. linux服务器自动压缩图片,Linux下压缩和优化jpg与png图片的方法
  4. 电脑市场Ghost XP SP2纯净版v3.0
  5. 如何将计算机中的文件添加到桌面,如何将日历添加至电脑桌面中
  6. 使用HttpClient访问网路数据
  7. 安徽省阜阳市谷歌卫星地图下载(百度网盘离线包下载)
  8. vos3000手机区号添加
  9. 计算机专业16字口号大全,口号大全霸气十足16字班级口号霸气押韵
  10. 大容量充电宝哪种好?充电宝小巧容量大推荐