【C#设计模式】总目录—共23种

【C#设计模式】装饰模式(Decorator Pattern)【结构型】

  • 前言
  • 一、模式的详细介绍
    • 1.1 动机(Motivate)
    • 1.2 意图(Intent)
    • 1.3 结构图(Structure)
    • 1.4 模式的组成
    • 1.5 模式的具体实现
  • 二、模式的实现要点
    • 2.1 模式的优点
    • 2.2 模式的缺点
    • 2.3 模式使用的场景
  • 三、.NET 中的实现
  • 总结
    • 个人学习理解:
  • 补充实例:

特别说明:主要通过实例代码来理解!

前言

今天我们要讲【结构型】设计模式的第三个模式,该模式是【装饰模式】,英文名称:Decorator Pattern。我第一次看到这个名称想到的是另外一个词语“装修”,我就说说我对“装修”的理解吧,大家一定要看清楚,是“装修”,不是“装饰”。我们长大了,就要结婚,要结婚就涉及到要买房子,买的精装修或者简单装修就可以住的,暂时不谈。我们就谈谈我们购买的是毛坯房。如果我想要房子的内饰是大理石风格的,我们只要在毛坯房的基础之上用大理石风格的材料装修就可以,我们当然不可能为了要一个装修风格,就把刚刚盖好的房子拆了在重新来过。房子装修好了,我们就住了进来,很开心。过了段时间,我们发现我们的房子在冬季比较冷,于是我就想给我们的房子增加保暖的功能,装修好的房子我们可以继续居住,我们只是在房子外面加一层保护层就可以了。又过了一段时间,总是有陌生人光顾,所以我们想让房子更安全,于是我们在外墙和房顶加装安全摄像头,同时门窗也增加安全系统。随着时间的流逝,我们可能会根据我们的需求增加相应的功能,期间,我们的房子可以正常使用,加上什么设施就有了相应的功能。从这一方面来讲,“装修”和“装饰”有类似的概念,接下来就让我们看看装饰模式具体是什么吧!

一、模式的详细介绍

1.1 动机(Motivate)

在房子装修的过程中,各种功能可以相互组合,来增加房子的功用。类似的,如果我们在软件系统中,要给某个类型或者对象增加功能,如果使用“继承”的方案来写代码,就会出现子类暴涨的情况。比如:IMarbleStyle是大理石风格的一个功能,IKeepWarm是保温的一个接口定义,IHouseSecurity是房子安全的一个接口,就三个接口来说,House是我们房子,我们的房子要什么功能就实现什么接口,如果房子要的是复合功能,接口不同的组合就有不同的结果,这样就导致我们子类膨胀严重,如果需要在增加功能,子类会成指数增长。【如下,有7种组合方式,就有7个子类】

House1:IMarbleStyle
House2:IKeepWarm
House3:IHouseSecurity
House4:IMarbleStyle,IKeepWarm
House5:IMarbleStyle,IHouseSecurity
House6:IKeepWarm,IHouseSecurity
House7:IMarbleStyle,IKeepWarm,IHouseSecurity

这个问题的根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质(所谓静态特质,就是说如果想要某种功能,我们必须在编译的时候就要定义这个类,这也是强类型语言的特点。静态,就是指在编译的时候要确定的东西;动态,是指运行时确定的东西),使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态(即运行时)地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

1.2 意图(Intent)

动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。   —— 《设计模式》GoF

1.3 结构图(Structure)

1.4 模式的组成

在装饰模式中的各个角色有:

(1)抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象。

(2)具体构件角色(Concrete Component):定义一个将要接收附加责任的类。

(3)装饰角色(Decorator):持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。

(4)具体装饰角色(Concrete Decorator):负责给构件对象添加上附加的责任。

1.5 模式的具体实现

刚开始一看这个“装饰模式”是有点不太好理解,既然这个模式是面向对象的设计模式,那在现实生活中一定有事例和其对应,其实这种例子也不少,大家好好的挖掘吧,也可以提高我们对面向对象的理解。我继续拿盖房子来说事吧。

using System;//renovation翻新,整修namespace 装饰模式的实现
{/// <summary>/// 该抽象类就是房子抽象接口的定义,该类型就相当于是Component类型,是饺子馅,需要装饰的,需要包装的/// </summary>public abstract class House{//房子的装修方法--该操作相当于Component类型的Operation方法public abstract void Renovation();}/// <summary>/// 该抽象类就是装饰接口的定义,该类型就相当于是Decorator类型,如果需要具体的功能,可以子类化该类型/// </summary>public abstract class DecorationStrategy : House //关键点之二,体现关系为Is-a,有了这个关系,装饰的类也可以继续装饰了{//通过组合方式引用Decorator类型,该类型实施具体功能的增加//这是关键点之一,包含关系,体现为Has-aprotected House _house;//通过构造器注入,初始化平台实现protected DecorationStrategy(House house){this._house = house;}//该方法就相当于Decorator类型的Operation方法public override void Renovation(){if (this._house != null){this._house.Renovation();}}}/// <summary>/// StarDeng的房子,我要按我的要求做房子,相当于ConcreteComponent类型,这就是我们具体的饺子馅,我个人比较喜欢韭菜馅/// </summary>public sealed class StarDengHouse : House{public override void Renovation(){Console.WriteLine("装修StarDeng的房子");}}/// <summary>/// 具有安全功能的设备,可以提供监视和报警功能,相当于ConcreteDecoratorA类型/// </summary>public sealed class HouseSecurityDecorator : DecorationStrategy{public HouseSecurityDecorator(House house) : base(house) { }public override void Renovation(){base.Renovation();Console.WriteLine("增加安全系统");}}/// <summary>/// 具有保温接口的材料,提供保温功能,相当于ConcreteDecoratorB类型/// </summary>public sealed class KeepWarmDecorator : DecorationStrategy{public KeepWarmDecorator(House house) : base(house) { }public override void Renovation(){base.Renovation();Console.WriteLine("增加保温的功能");}}public class Program{static void Main(){//这就是我们的饺子馅,需要装饰的房子House myselfHouse = new StarDengHouse();DecorationStrategy securityHouse = new HouseSecurityDecorator(myselfHouse);securityHouse.Renovation();//房子就有了安全系统了//【1】如果我既要安全系统又要保暖呢,继续装饰就行//DecorationStrategy securityAndWarmHouse = new KeepWarmDecorator(myselfHouse);//securityAndWarmHouse.Renovation();//【2】如果我既要安全系统又要保暖呢,继续装饰就行【和上边的进行运行比对】//【对运行结果难理解的话,打断点单步执行进行理解!!!】DecorationStrategy securityAndWarmHouse = new KeepWarmDecorator(securityHouse);securityAndWarmHouse.Renovation();Console.ReadLine();}}
}

代码中注释为【1】的运行结果:

代码中注释为【2】的运行结果:

二、模式的实现要点

1、通过采用组合、而非继承的手法,Decorator模式实现了在运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。

2、Component类在Decorator模式中充当抽象接口的角色,不应该去实现具体的行为。而且Decorator类对于Component类应该透明——换言之Component类无需知道Decorator类,Decorator类是从外部来扩展Component类的功能

3、Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。我们可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。

4、Decorator模式并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

2.1 模式的优点

(1)把抽象接口与其实现解耦。

(2)抽象和实现可以独立扩展,不会影响到对方。

(3)实现细节对客户透明,对用户隐藏了具体实现细节。

2.2 模式的缺点

(1)增加了系统的复杂度

2.3 模式使用的场景

(1)如果一个系统需要在构件的抽象化角色和具体化角色之间添加更多的灵活性,避免在两个层次之间建立静态的联系。

(2)设计要求实现化角色的任何改变不应当影响客户端,或者实现化角色的改变对客户端是完全透明的。

(3)需要跨越多个平台的图形和窗口系统上。

(4)一个类存在两个独立变化的维度,且两个维度都需要进行扩展。

三、.NET 中的实现

在Net框架中,有一个类型很明显的使用了“装饰模式”,这个类型就是Stream。Stream类型是一个抽象接口,它在System.IO命名空间里面,它其实就是Component。FileStream、NetworkStream、MemoryStream都是实体类ConcreteComponent。右边的BufferedStream、CryptoStream是装饰对象,它们都是继承了Stream接口的。

Stream就相当于Component,定义装饰的对象,FileStream就是要装饰的对象,BufferedStream是装饰对象。我们看看BufferedStream的部分定义。

 public sealed class BufferedStream : Stream{private const int _DefaultBufferSize = 4096;private Stream _stream;

结构很简单,对比结构图看吧,没什么可说的了。

总结

今天的文章就写到这里了,总结一下我对这个模式的看法,这个模式有点像包饺子,ConcreteComponent其实是饺子馅,Decorator就像饺子皮一样,包什么皮就有什么的样子,皮和皮也可以嵌套,当然我们生活中的饺子只是包一层。其实手机也是一个装饰模式使用的好例子,以前我们的手机只是接打电话,然后可以发短信和彩信,我在装饰一个就可以拍照了。我们现在的手机功能很丰富,其结果也类似装饰的结果。随着社会的进步,技术发展,模块化的手机也出现了,其设计原理也和“装饰模式”就更接近了。不光手机,还有我们身边其他很多家用电器也有类似的发展经历,我们努力发现生活中的真理吧,然后再在软件环境中慢慢体会吧。

个人学习理解:

装饰模式 类似于房子装修,在多个模块叠加组成不同的实体时用。

补充实例:

一个更易理解的实例:

装饰模式为已有类动态附加额外的功能就像LOL、王者荣耀等类Dota游戏中,英雄升级一样。每次英雄升级都会附加一个额外技能点学习技能。具体的英雄就是ConcreteComponent,技能栏就是装饰器Decorator,每个技能就是ConcreteDecorator;

using System;namespace ConsoleApp3
{//Component 英雄接口 public abstract class Hero{//学习技能public abstract void learnSkills();}//ConcreteComponent 具体英雄盲僧public sealed class BlindMonk : Hero{private String name;public BlindMonk(String name){this.name = name;}public override void learnSkills(){Console.WriteLine(name + "学习了以下技能!");}}//Decorator 技能栏public abstract class Skills : Hero{//持有一个英雄对象接口private Hero hero;public Skills(Hero hero){this.hero = hero;}public override void learnSkills(){if (hero != null)hero.learnSkills();}}//ConreteDecorator 技能:Qpublic class Skill_Q : Skills{private String skillName;public Skill_Q(Hero hero, String skillName) : base(hero){this.skillName = skillName;}public override void learnSkills(){base.learnSkills();Console.WriteLine("学习了技能Q:" + skillName);}}//ConreteDecorator 技能:Wpublic class Skill_W : Skills{private String skillName;public Skill_W(Hero hero, String skillName) : base(hero){this.skillName = skillName;}public override void learnSkills(){base.learnSkills();Console.WriteLine("学习了技能W:" + skillName);}}//ConreteDecorator 技能:Epublic class Skill_E : Skills{private String skillName;public Skill_E(Hero hero, String skillName) : base(hero){this.skillName = skillName;}public override void learnSkills(){base.learnSkills();Console.WriteLine("学习了技能E:" + skillName);}}//ConreteDecorator 技能:Rpublic class Skill_R : Skills{private String skillName;public Skill_R(Hero hero, String skillName) : base(hero){this.skillName = skillName;}public override void learnSkills(){base.learnSkills();Console.WriteLine("学习了技能R:" + skillName);}}//客户端:召唤师public class Player{public static void Main(String[] args){//选择英雄Hero hero = new BlindMonk("李青");Skills r = new Skill_R(hero, "猛龙摆尾");Skills e = new Skill_E(r, "天雷破/摧筋断骨");Skills w = new Skill_W(e, "金钟罩/铁布衫");Skills q = new Skill_Q(w, "天音波/回音击");//【1】学习技能【分别理解下边4行代码!对于整个代码理解很重要!】r.learnSkills(); Console.WriteLine("————————————————");e.learnSkills(); Console.WriteLine("————————————————");w.learnSkills(); Console.WriteLine("————————————————");q.learnSkills(); Console.WriteLine("————————————————");//【2】学习技能Hero hero1 = new BlindMonk("李青1");Skills q1 = new Skill_Q(hero1, "天音波/回音击");q1.learnSkills();Console.ReadLine();}}
}

运行结果:

【C#设计模式】总目录—共23种

【C#设计模式】8.装饰模式(Decorator Pattern)【结构型】相关推荐

  1. 二十四种设计模式:装饰模式(Decorator Pattern)

    装饰模式(Decorator Pattern) 介绍 动态地给一个对象添加一些额外的职责.就扩展功能而言,它比生成子类方式更为灵活. 示例 有一个Message实体类,某个对象对它的操作有Insert ...

  2. .NET设计模式(15):结构型模式专题总结

    .NET设计模式(15):结构型模式专题总结 --探索设计模式系列之十五 Terrylee,2006年5月 摘要:结构型模式,顾名思义讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模 ...

  3. 设计模式之美总结(结构型篇)

    title: 设计模式之美总结(结构型篇) date: 2022-12-21 09:59:11 tags: 设计模式 categories: 设计模式 cover: https://cover.png ...

  4. 设计模式(3)——装饰者模式(Decorator,结构型)

    1.概述 使用设计模式可以提高代码的可复用性.可扩充性和可维护性.装饰者模式( Pattern)属于结构型模式,动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案.它是通过创 ...

  5. 设计模式系列之装饰模式(Decorator Pattern)

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

  6. 设计模式初识(三)结构型模式(Structural Pattern)

    为什么要使用结构型模式 结构型模式关注点在于"如何组合对象/类",更关注类之间的组合关系: 类结构型模式关心类的组合,由多个类可以组合成一个更大的(继承): 对象结构型模式关心类与 ...

  7. 23种设计模式:(二)结构型模式

    根据北京尚学堂的视频所学习 结构型模式: 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题. 分类: 适配器模式.代理模式.桥接模式. 装饰模式.组合模式.外观模式. ...

  8. 设计模式之装饰模式(Decorator)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  9. JAVA设计模式(09):结构型-代理模式(Proxy)

    代理模式是经常使用的结构型设计模式之中的一个,当无法直接訪问某个对象或訪问某个对象存在困难时能够通过一个代理对象来间接訪问,为了保证client使用的透明性,所訪问的真实对象与代理对象须要实现同样的接 ...

  10. 《精通python设计模式》读书笔记之——结构型设计模式

    结构型设计模式: 结构型设计模式处理一个系统中不同实体(比如,类和对象)之间的关系,关注的是提供一种简单的对象组合方式来创造新功能.可用于实现不兼容软件之间的接口兼容. ①.适配器模式 简介: 适配器 ...

最新文章

  1. ”计算机操作系统“学习笔记1
  2. Linux SSH远程文件/目录传输命令scp
  3. bugku——web 做题记录
  4. 封装 Vue.js 组件库
  5. 有关迷宫的c语言程序的编写,3种C语言编写走迷宫的方法
  6. 混合列压缩(HCC)在OLAP及OLTP场景中的测试
  7. 02数据库表的相关操作
  8. PCIe规范的扩展:SR-IOV(Single Root I/O virtual)网卡直通技术
  9. Cocos Shader入门基础六:平面、双面材质与自定义裁剪面 来自虚空的龙
  10. 沟通CTBS物业管理行业远程接入解决方案
  11. HTML5+CSS大作业——清新春暖花开个人博客网站(6页)
  12. ——黑马程序员——OC中Foundation下NSFileManager的使用
  13. [分享]加拿大渥太华市卡尔顿大学John W. Chinneck教授谈如何组织论文
  14. 网络三剑客之sed编辑器
  15. 写在第35个教师节到来时的话——对我国现阶段职业教育改革的总体论述
  16. 迅捷路由器造成计算机无法上网,迅捷无线路由器设置好却不能上网
  17. VScode 无法编译
  18. c# 微信公众号开发之token验证第三方服务器
  19. jdbc批量插入的4种方式【百万条数据插入只需几秒】
  20. 姿态和位置,四旋翼的控制流程

热门文章

  1. 通过Jscript中@cc_on 语句识别IE浏览器及版本的代码
  2. 松下A6伺服调试笔记
  3. 利用CvVideoWriter和cvWriteFrame函数合成视频
  4. 五丶python_frm组件(吴老师)
  5. 天道酬勤,专科出身的我,2年进入苏宁,5年跳槽阿里,我是怎么快速晋升的呢?
  6. pycharm进入科学模式以及退出
  7. RF踩坑之调用pyautogui鼠标操作报错
  8. 经典原理法则效应(转)
  9. SQL语法范例大全(SQL Server、Oracle 通解)
  10. java jmx 远程_java – 无法远程连接到JMX?