C#设计模式(9)——装饰者模式(Decorator Pattern)

一、引言

在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类)、AccessoriesPhone(挂件手机类)等,这样就会导致 ”子类爆炸“问题,为了解决这个问题,我们可以使用装饰者模式来动态地给一个对象添加额外的职责。下面让我们看看装饰者模式。

二、装饰者模式的详细介绍

2.1 定义

装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任,装饰者模式相比生成子类可以更灵活地增加功能。

2.2 装饰者模式实现

这里以手机和手机配件的例子来演示装饰者模式的实现,具体代码如下:

/// <summary>/// 手机抽象类,即装饰者模式中的抽象组件类/// </summary>public abstract class Phone{public abstract void Print();}/// <summary>/// 苹果手机,即装饰着模式中的具体组件类/// </summary>public class ApplePhone:Phone{/// <summary>/// 重写基类方法/// </summary>public override void Print(){Console.WriteLine("开始执行具体的对象——苹果手机");}}/// <summary>/// 装饰抽象类,要让装饰完全取代抽象组件,所以必须继承自Photo/// </summary>public abstract class Decorator:Phone{private Phone phone;public Decorator(Phone p){this.phone = p;}public override void Print(){if (phone != null){phone.Print();}}}/// <summary>/// 贴膜,即具体装饰者/// </summary>public class Sticker : Decorator{public Sticker(Phone p): base(p){ }public override void Print(){base.Print();// 添加新的行为
            AddSticker();      }/// <summary>/// 新的行为方法/// </summary>public void AddSticker(){Console.WriteLine("现在苹果手机有贴膜了");}}/// <summary>/// 手机挂件/// </summary>public class Accessories : Decorator{public Accessories(Phone p): base(p){}public override void Print(){base.Print();// 添加新的行为
            AddAccessories();          }/// <summary>/// 新的行为方法/// </summary>public void AddAccessories(){Console.WriteLine("现在苹果手机有漂亮的挂件了");}}

此时客户端调用代码如下:

class Customer{static void Main(string[] args){// 我买了个苹果手机Phone phone = new ApplePhone();// 现在想贴膜了Decorator applePhoneWithSticker = new Sticker(phone);// 扩展贴膜行为
            applePhoneWithSticker.Print();Console.WriteLine("----------------------\n");// 现在我想有挂件了Decorator applePhoneWithAccessories = new Accessories(phone);// 扩展手机挂件行为
            applePhoneWithAccessories.Print();Console.WriteLine("----------------------\n");// 现在我同时有贴膜和手机挂件了Sticker sticker = new Sticker(phone);Accessories applePhoneWithAccessoriesAndSticker = new Accessories(sticker);applePhoneWithAccessoriesAndSticker.Print();Console.ReadLine();}

从上面的客户端代码可以看出,客户端可以动态地将手机配件增加到手机上,如果需要添加手机外壳时,此时只需要添加一个继承Decorator的手机外壳类,从而,装饰者模式扩展性也非常好。

2.3 装饰者模式的类图

实现完了装饰者模式之后,让我们看看装饰者模式实现中类之间的关系,具体见下图:

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

  • 抽象构件(Phone)角色:给出一个抽象接口,以规范准备接受附加责任的对象。
  • 具体构件(AppPhone)角色:定义一个将要接收附加责任的类。
  • 装饰(Dicorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
  • 具体装饰(Sticker和Accessories)角色:负责给构件对象 ”贴上“附加的责任。

 

三、装饰者模式的优缺点

看完装饰者模式的详细介绍之后,我们继续分析下它的优缺点。

优点:

  1. 装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活
  2. 通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合
  3. 装饰者模式有很好地可扩展性

缺点:装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。

四、使用场景

下面让我们看看装饰者模式具体在哪些情况下使用,在以下情况下应当使用装饰者模式:

  1. 需要扩展一个类的功能或给一个类增加附加责任。
  2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能

五、.NET中装饰者模式的实现

在.NET 类库中也有装饰者模式的实现,该类就是System.IO.Stream,下面看看Stream类结构:

上图中,BufferedStream、CryptoStream和GZipStream其实就是两个具体装饰类,这里的装饰者模式省略了抽象装饰角色(Decorator)。下面演示下客户端如何动态地为MemoryStream动态增加功能的。

 MemoryStream memoryStream = new MemoryStream(new byte[] {95,96,97,98,99});// 扩展缓冲的功能BufferedStream buffStream = new BufferedStream(memoryStream);// 添加加密的功能CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);// 添加压缩功能GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);

六、总结

到这里,装饰者模式的介绍就结束了,装饰者模式采用对象组合而非继承的方式实现了再运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的 ”灵活性差“和”多子类衍生问题“。同时它很好地符合面向对象设计原则中 ”优先使用对象组合而非继承“和”开放-封闭“原则

 

本专题所有源码:设计模式之装饰者模式

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

北大青鸟c语言课后答案,北大青鸟C语言教程--第一章 C语言基础.ppt

《北大青鸟C语言教程--第一章 C语言基础.ppt》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《北大青鸟C语言教程--第一章 C语言基础.ppt(20页珍藏版)》请在人人文库网上搜索。1、第一章,C 语言基础,2,课程地位,.Net ,以 # 开始的语句称为预处理器指令,#include语句不…

nosql_探索NoSQL系列

nosql数据科学 (Data Science) Knowledge on NoSQL databases seems to be an increasing requirement in data science applications, yet, the taxonomy is so diverse and problem-centered that it can be a challenge to grasp them. This post attempts to shed light on…

C++TCP和UDP属于传输层协议

TCP和UDP属于传输层协议。其中TCP提供IP环境下的数据可靠传输&#xff0c;它事先为要发送的数据开辟好连接通道&#xff08;三次握手&#xff09;&#xff0c;然后再进行数据发送&#xff1b;而UDP则不为IP提供可靠性&#xff0c;一般用于实时的视频流传输&#xff0c;像rtp、r…

程序员如何利用空闲时间挣零花钱

一&#xff1a; 私活 作为一名程序员&#xff0c;在上班之余&#xff0c;我们有大把的时间&#xff0c;不能浪费&#xff0c;这些时间其实都是可以用来挖掘自己潜在的创造力&#xff0c;今天要讨论的话题就是&#xff0c;程序员如何利用空余时间挣零花钱&#xff1f;比如说周末…

编写程序乘法口诀表C语言,陈广川问:c语言编程九九乘法口诀表 怎样用c语言写九九乘法口诀表?...

怎样用c语言写九九乘法口诀表&#xff1f;哈哈&#xff0c;我刚刚用javascript写好乘法口诀表。C语言&#xff0c;如何编写程序输出九九乘法表。形式如下 ********* ******** ******* ****** ***** **** *** ** *&#xff1f;两个循环&#xff0c;一般用for循环 一个循环控制行…

PHP中文乱码解决办法

一&#xff0e;首先是PHP网页的编码 1. php文件本身的编码与网页的编码应匹配 a. 如果欲使用gb2312编码&#xff0c;那么php要输出头&#xff1a;header(“Content-Type: text/html; charsetgb2312")&#xff0c;静态页面添加<meta http-equiv"Content-T…

python中api_通过Python中的API查找相关的工作技能

python中api工作技能世界 (The World of Job Skills) So you want to figure out where your skills fit into today’s job market. Maybe you’re just curious to see a comprehensive constellation of job skills, clean and standardized. Or you need a taxonomy of ski…

欺诈行为识别_使用R(编程)识别欺诈性的招聘广告

欺诈行为识别背景 (Background) Online recruitment fraud (ORF) is a form of malicious behaviour that aims to inflict loss of privacy, economic damage or harm the reputation of the stakeholders via fraudulent job advertisements.在线招聘欺诈(ORF)是一种恶意行为…

PE文件的感染C++源代码

PE文件的感染C源代码 PE文件规定了可执行文件的格式&#xff0c;凡是符合此格式的文件都能在windows系统上运行。PE文件的格式暂且不谈&#xff0c;说一些感染PE文件的几种途径。 导入表感染。这个涉及比较复杂的操作&#xff0c;首先&#xff0c;要自行写一个dll文件&#x…

c语言实验四报告,湖北理工学院14本科C语言实验报告实验四数组

湖北理工学院14本科C语言实验报告实验四 数组.doc实验四 数 组实验课程名C语言程序设计专业班级 14电气工程2班 学号 201440210237 姓名 熊帆 实验时间 5.12-5.26 实验地点 K4-208 指导教师 祁文青 一、实验目的和要求1. 掌握一维数组和二维数组的定义、赋值和输入输出的方法&a…

c语言宏定义

一. #define是C语言中提供的宏定义命令&#xff0c;其主要目的是为程序员在编程时提供一定的方便&#xff0c;并能在一定程度上提高程序的运行效率&#xff0c;但学生在学习时往往不能理解该命令的本质&#xff0c;总是在此处产生一些困惑&#xff0c;在编程时误用该命令&#…

rabbitmq channel参数详解【转】

1、Channel 1.1 channel.exchangeDeclare()&#xff1a; type&#xff1a;有direct、fanout、topic三种durable&#xff1a;true、false true&#xff1a;服务器重启会保留下来Exchange。警告&#xff1a;仅设置此选项&#xff0c;不代表消息持久化。即不保证重启后消息还在。原…

感染EXE文件代码(C++)

C代码#include <windows.h> #include <winnt.h> #include <stdio.h> #include <assert.h> #define DEBUG 1 #define EXTRA_CODE_LENGTH 18 #define SECTION_SIZE 0x1000 #define SECTION_NAME ".eViLhsU" #define F…

nlp gpt论文_GPT-3:NLP镇的最新动态

nlp gpt论文什么是GPT-3&#xff1f; (What is GPT-3?) The launch of Open AI’s 3rd generation of the pre-trained language model, GPT-3 (Generative Pre-training Transformer) has got the data science fraternity buzzing with excitement!Open AI的第三代预训练语言…

真实不装| 阿里巴巴新人上路指北

新手上路&#xff0c;总想听听前辈们分享他们走过的路。橙子选取了阿里巴巴合伙人逍遥子&#xff08;阿里巴巴集团CEO&#xff09; 、Eric&#xff08;蚂蚁金服董事长兼CEO&#xff09;、Judy&#xff08;阿里巴巴集团CPO&#xff09;的几段分享&#xff0c;他们是如何看待职场…

小程序学习总结

上个周末抽空了解了一下小程序,现在将所学所感记录以便日后翻看;需要指出的是我就粗略过了下小程序的api了解了下小程序的开发流程以及工具的使用,然后写了一个小程序的demo;在我看来,如果有前端基础学习小程序无异于锦上添花了,而我这个三年的码农虽也写过不少前端代码但离专业…

tomcat java环境配置

jsp 环境变量配置 一、配置JDK 首先&#xff0c;从Sun网站上下载jdk。 双击jdk-1_5_0_04-windows-i586-p.exe开始安装&#xff0c;默认安装到C:/Program Files/Java/jdk1.5.0_04&#xff0c;你也可以更改路径&#xff0c;但要记住最后选择的路径&#xff0c;设置环境变量的时候…

uber 数据可视化_使用R探索您在Uber上的活动:如何分析和可视化您的个人数据历史记录

uber 数据可视化Perhaps, dear reader, you are too young to remember that before, the only way to request a particular transport service such as a taxi was to raise a hand to make a signal to an available driver, who upon seeing you would stop if he was not …

java B2B2C springmvc mybatis电子商城系统(四)Ribbon

2019独角兽企业重金招聘Python工程师标准>>> 一&#xff1a;Ribbon是什么&#xff1f; Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法&#xff0c;将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如…

c语言函数的形参有几个,C中子函数最多有几个形参

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼C89 31个&#xff0c;C99 127个。ANSI C892.2.4.1 Translation limitsThe implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following lim…