c#装饰器模式详解
基础介绍:
动态地给一个对象添加一些额外的职责。适用于需要扩展一个类的功能,或给一个类添加多个变化的情况。
装饰器,顾名思义就是在原有基础上添加一些功能。
大家都只知道如果想单纯的给原有类增加一些功能,可以直接继续该类生成一个子类就可以。
举个例子,如果现在有个手机类,想给手机贴膜,传统的做法就是新建一个手机类的子类(手机贴膜子类),继承自手机类。
使用这个子类就可以完成对手机的贴膜操作。
那如果又想给手机按保护壳的话,传统做法有两种,可以继续新建一个手机类的子类(手机保护壳子类),继承自手机类。
使用这个子类可以给手机按保护壳,但也就失去了给手机贴膜的功能。另一种做法,新建一个手机贴膜类的子类(手机贴膜+保护壳),也就是手机类的子子类。
这样即可以贴膜也可以按手机壳。
大家思考一个问题,如果有很多个装饰并且想随意组合的话,那就有N个子类并且存在很深的继承链路。
想要解决这个问题,就可以用到装饰器了。
比如贴膜装饰、保护壳装饰、贴纸装饰等等,它们都是独立存在的,只继承自装饰器类。
什么意思呢?就是说给手机贴膜的时候它并不会给手机按保护壳的功能,职责单一,贴膜装饰器只负责给手机贴膜。
这样做有什么好处呢?好处就是这些装饰可以随意组合,比如即想贴膜又想按保护壳,就可以将贴膜装饰+保护壳装饰进组组合。
在装饰器模式中各个角色有:
- 抽象构件(Component)角色:规范手机的构成
- 具体构件(ConcreteComponent)角色:继承自抽象构件,具体实现手机。(大多情况下,可以省略抽象构件,抽象装饰类可以直接继承)
- 抽象装饰类(Decorator)角色:创建一个构件(Component)对象的实例,可以使用这个实例调用原构件的功能,并规范装饰类。
- 具体装饰类(ConcreteDecorator)角色:继承自抽象装饰类,具体实现该装饰功能。
应用场景:
原有类无法修改或者修改困难的情况下,对类进行多次扩展或功能性比较相互独立,有效防止多次扩展的情况下子类的膨胀。
注:如果类的扩展比较简单,并且不会多次进行扩展的情况下直接使用类的继承生成子类的方式更为方便快捷。
创建方式:
为了方便说明,以下实例就不创建抽象构件了。
首先先看下不使用装饰器进行类的扩展

1 /// <summary>
2 /// 手机类
3 /// </summary>
4 public class Phone
5 {
6 public void Print()
7 {
8 Console.WriteLine("手机");
9 }
10 }
11
12 /// <summary>
13 /// 手机贴膜
14 /// </summary>
15 public class Sticker : Phone
16 {
17 public Sticker()
18 {
19 base.Print();
20 }
21
22 /// <summary>
23 /// 进行贴膜
24 /// </summary>
25 public void AddSticker()
26 {
27 Console.WriteLine("给手机贴膜");
28 }
29 }
30
31 /// <summary>
32 /// 手机保护壳
33 /// </summary>
34 public class ProtectiveCase : Phone
35 {
36 public ProtectiveCase()
37 {
38 base.Print();
39 }
40
41 /// <summary>
42 /// 按保护壳
43 /// </summary>
44 public void AddProtectiveCase()
45 {
46 Console.WriteLine("给手机按保护壳");
47 }
48 }
49
50 /// <summary>
51 /// 即贴膜又按保护壳
52 /// </summary>
53 public class ProtectiveCaseAndSticker : Sticker
54 {
55 public ProtectiveCaseAndSticker()
56 {
57 }
58
59 /// <summary>
60 /// 按保护壳
61 /// </summary>
62 public void AddProtectiveCase()
63 {
64 Console.WriteLine("给手机按保护壳");
65 }
66 }
67
68 /// <summary>
69 /// 客户端
70 /// </summary>
71 class Client
72 {
73 static void Main(string[] args)
74 {
75 //创建一个手机
76 Phone phone = new Phone();
77 phone.Print();
78 Console.WriteLine("\r\n");
79
80 //给手机贴膜
81 Sticker sticker = new Sticker();
82 sticker.AddSticker();
83 Console.WriteLine("\r\n");
84
85 //给手机按保护壳
86 ProtectiveCase protectiveCase = new ProtectiveCase();
87 protectiveCase.AddProtectiveCase();
88 Console.WriteLine("\r\n");
89
90 //即贴膜又按保护壳
91 ProtectiveCaseAndSticker protectiveCaseAndSticker = new ProtectiveCaseAndSticker();
92 protectiveCaseAndSticker.AddSticker();
93 protectiveCaseAndSticker.AddProtectiveCase();
94 Console.ReadKey();
95 }
96 }
通过上述实例可以看出,如果各个扩展功能比较独立的话可以直接进行继承扩展。
如果各个功能直接有交集的情况下,会造成很深的继承关系。
比如上述实例中,如果单独贴膜或者单独安装保护壳则直接继承手机类即可。
但如果想要即贴膜又要安装保护壳,各自继承手机类的方式就行不通了,只能在贴膜类或者保护壳类的基础上进行扩展。
如果还有添加手机挂饰,那就还需要再一层继承关系。
要解决这个问题就用到了装饰器,下面看看使用装饰器是怎么给手机添加新功能的。
使用装饰器模式对类进行扩展

1 /// <summary>
2 /// 手机类
3 /// </summary>
4 public class Phone
5 {
6 public void Print()
7 {
8 Console.WriteLine("手机");
9 }
10 }
11
12 /// <summary>
13 /// 装饰抽象类
14 /// </summary>
15 public abstract class Decorator : Phone
16 {
17 private Phone phone;
18
19 public Decorator(Phone p)
20 {
21 this.phone = p;
22 }
23
24 public abstract void AddDecorator();
25 }
26
27 /// <summary>
28 /// 贴膜装饰
29 /// </summary>
30 public class Sticker : Decorator
31 {
32 public Sticker(Phone p)
33 : base(p)
34 {
35 }
36
37 public override void AddDecorator()
38 {
39 Console.WriteLine("给手机贴膜");
40 }
41 }
42
43 /// <summary>
44 /// 保护壳装饰
45 /// </summary>
46 public class ProtectiveCase : Decorator
47 {
48 public ProtectiveCase(Phone p)
49 : base(p)
50 {
51 }
52
53 /// <summary>
54 /// 按保护壳
55 /// </summary>
56 public override void AddDecorator()
57 {
58 Console.WriteLine("给手机按保护壳");
59 }
60 }
61
62 /// <summary>
63 /// 客户端
64 /// </summary>
65 class Client
66 {
67 static void Main(string[] args)
68 {
69 //单独给手机贴膜
70 Phone phone = new Phone();
71 phone.Print();
72 Decorator sticker = new Sticker(phone);
73 sticker.AddDecorator();
74
75 //单独给手机按保护壳
76 phone = new Phone();
77 phone.Print();
78 Decorator protectiveCase = new ProtectiveCase(phone);
79 protectiveCase.AddDecorator();
80 Console.WriteLine("\r\n");
81
82 //即贴膜又按保护壳
83 phone = new Phone();
84 phone.Print();
85 //首先创建贴膜装饰实例,将手机对象传入
86 Decorator decorator = new Sticker(phone);
87 //进行贴膜操作
88 decorator.AddDecorator();
89 //创建保护壳装饰实例,将贴膜后的手机对象传入
90 decorator = new ProtectiveCase(decorator);
91 //进行按保护壳操作
92 decorator.AddDecorator();
93 Console.ReadKey();
94 }
95 }
从上述实例中可以看出,各个装饰类只对装饰抽象类负责,职责单一。
各个装饰进行组合时,方便随意。新增装饰时,只需要新增一个继承自装饰抽象类的子类即可实现以原有装饰的随意组合使用。
总结:
想要扩展一个类的时候,传统的继承生成子类的形式,适用于扩展简单,并且不会多次扩展的情况下。
而如果一个类的扩展是周期性,多次扩展的或功能性比较相互独立的情况下,可以使用装饰器,可以有效的解决传统继承扩展子类膨胀的问题。
装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
c#装饰器模式详解的更多相关文章
- (十)装饰器模式详解(与IO不解的情缘)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...
- 涉及模式之 装饰器模式详解(与IO不解的情缘)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...
- Java 装饰器模式详解
转载请注明出处:http://blog.csdn.net/zhaoyanjun6/article/details/56488020 前言 在上面的几篇文章中,着重介绍了Java 中常见的 IO 相关知 ...
- Javascript设计模式之装饰者模式详解篇
一.前言: 装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象. 装饰者模式的特点: 1. 在不改 ...
- 【装饰器设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
简介 装饰器模式(Decorator Pattern)是一种结构型设计模式.将对象放入到一个特殊封装的对象中,为这个对象绑定新的行为,具备新的能力,同时又不改变其原有结构. 如果你希望在无需修改代码的 ...
- python装饰器大详解
1.作用域 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我要理解两点:a.在全局不能访问到局部定 ...
- python 函数及变量作用域及装饰器decorator @详解
一.函数及变量的作用 在python程序中,函数都会创建一个新的作用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,因为Python一切都是对象,而在命名空间中 ...
- python装饰器使用详解
装饰器 '''装饰器:就是闭包(闭包的一个应用场景) -- 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数 优点: -- 丰富了原有函数的功能 -- 提高了程序的可拓展性''' 开 ...
- 进阶Python:装饰器 全面详解
进阶Python:装饰器 前言 前段时间我发了一篇讲解Python调试工具PySnooper的文章,在那篇文章开始一部分我简单的介绍了一下装饰器,文章发出之后有几位同学说"终于了解装饰器的用 ...
- python装饰器学习详解-函数部分
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 最近阅读<流畅的python>看见其用函数写装饰器部分写的很好,想写一些自己的读书笔记. ...
随机推荐
- Failed to connect to 127.0.0.1 port 1080: Connection refused拒绝连接错误
一.git拒绝连接原因分析 使用git从远程仓库下载代码出现上述的错误是因为使用了proxy代理,所以要解决该问题,核心操作就是要取消代理 二.解决方式 1.查看Linux当前有没有使用代理 通过gi ...
- axios详解以及完整封装方法
""" 一.axios是什么 Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中. 它是 isomorphic 的(即同一套代码可以运行 ...
- javascript中一些难以理解的专有名词 2(也不是很专有)
作用域链 让人迷惑的例子 function foo() {console.log(v)} function foo1() { var v = "v1" foo() console. ...
- Robot Framework 自动化测试随笔(二)
二.Web自动化(1) 1.安装selenium2library库 pip install robotframework-selenium2library 2.指定报告的生成路径 在[Run]标签 ...
- Go中 net/http 使用
转载请注明出处: net/http是Go语言标准库中的一个包,提供了实现HTTP客户端和服务器的功能.它使得编写基于HTTP协议的Web应用程序变得简单和方便. net/http包的主要用途包括: 实 ...
- Java 生态需要新鲜的血液、需要狂飙的刺激。Solon v2.4.1 发布
Solon 是什么开源项目? 一个,Java 新的生态型应用开发框架.它从零开始构建,有自己的标准规范与开放生态(历时五年,已有全球第二级别的生态规模).与其他框架相比,它解决了两个重要的痛点:启动慢 ...
- quarkus依赖注入之七:生命周期回调
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇的知识点是bean的生命周期回调:在be ...
- pyqt5学习日记
前提需要pip安装PyQt5与PyQt5-tools 安装后会有qtdesigner.exe和pyuic5.exe,用everything直接可以搜索到 qtdesigner.exe是来设计ui的 p ...
- 论文解读(SimGCL)《Are Graph Augmentations Necessary? Simple Graph Contrastive Learning for Recommendation》
Note:[ wechat:Y466551 | 可加勿骚扰,付费咨询 ] 论文信息 论文标题:Are Graph Augmentations Necessary? Simple Graph Contr ...
- AI绘画StableDiffusion实操教程:月光下的美人(含高清图片)
本教程收集于:AIGC从入门到精通教程汇总 今天继续分享AI绘画实操教程,如何用lora包生成超真实好看质感超强的人物图片. 放大高清图已放到教程包内,需要的可以自取. 出图效果: 更多图片资源访问查 ...