C# 设计模式之装饰器模式
总目录
前言
装饰器模式的主要作用就是扩展一个类的功能,或给一个类添加多个变化的情况。学习面向对象的都知道,如果想单纯的给某个类增加一些功能,可以直接继承该类生成一个子类就可以。应对一些简单的业务场景继承也就够了,但是面对一些复杂的业务场景,仅靠继承是不够的。那么我们看看装饰器模式是如果以一个更为灵活的方式扩展一个对象的功能的。
1 基础介绍
- 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更为灵活。
- 适用于需要扩展一个类的功能,或给一个类添加多个变化的情况。
- 在装饰模式中的角色:
- 抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象。
- 具体构件角色(Concrete Component):定义一个将要接收附加责任的类。
- 装饰角色(Decorator):持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
- 具体装饰角色(Concrete Decorator):负责给构件对象添加上附加的责任。
2 使用场景
当需要扩展一个类的功能或给一个类增加附加责任,并且扩展功能可能存在多个且需要可动态组合配置的情况下,使用装饰器模式就是最好的解决办法
注:如果类的扩展比较简单,并且不会多次进行扩展的情况下直接使用类的继承生成子类的方式更为方便快捷。
3 实现方式
看了上面这些描述,没有案例我们是无法理解装饰器模式的精髓。那么我们现在通过一个手机贴膜,装手机壳的案例来理解一下。
1. 传统模式
(1) 第一个需求,张三有一个手机,现在想给贴膜,代码实现如下:
public class Phone{public virtual void Show() {Console.WriteLine("手机");}}//贴膜手机 继承自 手机类public class MoPhone : Phone{//贴膜的方法public void TieMo(){Console.WriteLine("给手机贴膜了!");}public override void Show(){base.Show();TieMo();}}
客户端调用:
static void Main(string[] args){Phone moPhone = new MoPhone();moPhone.Show();Console.ReadKey();}
(2) 现在需求改了,手机不贴膜了,要装手机壳,于是我们改代码:
//装手机壳的手机 继承自 手机类public class KePhone : Phone{//装手机壳的方法public void ZhuangKe(){Console.WriteLine("给手机装手机壳了!");}public override void Show(){base.Show();ZhuangKe();}}
客户端调用:
static void Main(string[] args){Phone phone = new KePhone();phone.Show();Console.ReadKey();}
(3) 现在需求又改了,手机贴膜 + 装手机壳,于是我们改代码:
public class Phone{public virtual void Show() {Console.WriteLine("手机");}}//贴膜手机 继承自 手机类public class MoPhone : Phone{//贴膜的方法public void TieMo(){Console.WriteLine("给手机贴膜了!");}public override void Show(){base.Show();TieMo();}}//现在又改变注意了,既想给手机贴膜也想给手机装手机壳//直接继承已有的贴膜手机类来实现会比较省事public class KeAndMoPhone : MoPhone{//装手机壳的方法public void ZhuangKe(){Console.WriteLine("给手机装手机壳了!");}public override void Show(){base.Show();ZhuangKe();}}
客户端调用:
static void Main(string[] args){Phone phone = new KeAndMoPhone();phone.Show();Console.ReadKey();}
上面的实例中,如果单独贴膜或者单独安装保护壳则直接继承手机类即可。
但如果想要即贴膜又要安装保护壳,各自继承手机类的方式就行不通了,只能在贴膜类或者保护壳类的基础上进行扩展。如果还有添加手机挂饰,那就还需要再一层继承关系,这样就会导致 ”子类爆炸“问题,为了解决这个问题就用到了装饰器,下面看看使用装饰器是怎么给手机添加新功能的。
2. 装饰器模式
1 首先定义手机抽象类 和 手机实现类
public abstract class AbstractPhone{public abstract void Show();}public class XiaoMiPhone : AbstractPhone{public override void Show(){Console.WriteLine("小米手机");}}
2 再定义一个装饰的抽象类
//装饰抽象类,是装饰模式的核心public abstract class Decorator : AbstractPhone{//保持对手机对象的引用protected AbstractPhone abstractPhone;public Decorator(AbstractPhone phone){abstractPhone = phone;}public override void Show(){//这行代码比较有意思,是实现装饰模式的巧思abstractPhone?.Show();}}
3 定义装饰抽象类的实现:贴膜装饰,装手机壳装饰
// 贴膜装饰类,主要实现给手机贴膜的扩展功能public class MoPhone : Decorator{public MoPhone(AbstractPhone phone) : base(phone){}public override void Show(){base.Show();TieMo();}//扩展的功能:贴膜public void TieMo(){Console.WriteLine("给手机贴膜了!");}}
// 手机壳装饰类,主要实现给手机装手机壳的扩展功能public class KePhone : Decorator{public KePhone(AbstractPhone phone) : base(phone){}public override void Show(){base.Show(); ZhuangKe();}//扩展的功能:装手机壳public void ZhuangKe(){Console.WriteLine("给手机装手机壳了!");}}
客户端调用:
- 只给手机贴膜
static void Main(string[] args){AbstractPhone phone1 = new XiaoMiPhone();Decorator decorator1 = new MoPhone(phone1);decorator1.Show();Console.ReadKey();}
- 只给手机装手机壳
static void Main(string[] args){AbstractPhone phone2 = new XiaoMiPhone();Decorator decorator2 = new KePhone(phone2);decorator2.Show();Console.ReadKey();}
- 给手机贴膜+装手机壳
static void Main(string[] args){AbstractPhone phone3 = new XiaoMiPhone();Decorator decorator3 = new MoPhone(phone3);decorator3 = new KePhone(decorator3);decorator3.Show();Console.ReadKey();}
4 需求变更:现在想给手机 贴膜 + 玩偶吊坠
我们只需新增一个 玩偶吊坠 类 继承自 装饰抽象类,然后定义一个玩偶吊坠的装饰方法
// 玩偶吊坠装饰类,主要实现给手机装玩偶吊坠的扩展功能public class DiaoZhuiPhone : Decorator{public DiaoZhuiPhone(AbstractPhone phone) : base(phone){}public override void Show(){base.Show();ZhuangDiaoZhui();}//扩展功能:给手机装玩偶吊坠public void ZhuangDiaoZhui(){Console.WriteLine("给手机安装玩偶吊坠了!");}}
客户端调用:
static void Main(string[] args){//给手机贴膜+玩偶吊坠AbstractPhone phone = new XiaoMiPhone();Decorator decorator = new MoPhone(phone);decorator = new DiaoZhuiPhone(decorator);decorator.Show();Console.ReadKey();}
我们发现当我们想要给手机加新的装饰,只需简单的新增对应的装饰类,在装饰类定义一个扩展的装饰方法(新功能)即可。而且还可以对装饰类进行不同组合,这使得我们的代码非常的灵活。
4 优缺点分析
- 优点:
- 相较于继承,装饰器模式可以更为灵活的扩展新功能,并且避免了单独使用继承带来的 “多子类衍生问题“。
- 很好地符合面向对象设计原则中 ”优先使用对象组合而非继承“和”开放-封闭“原则。装饰者模式有很好地可扩展性。
- 缺点:装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。
结语
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
C#设计模式之八装饰模式(Decorator Pattern)【结构型】
c#中装饰器模式详解
C#设计模式(9)——装饰者模式(Decorator Pattern)
相关文章:
C# 设计模式之装饰器模式
总目录 前言 装饰器模式的主要作用就是扩展一个类的功能,或给一个类添加多个变化的情况。学习面向对象的都知道,如果想单纯的给某个类增加一些功能,可以直接继承该类生成一个子类就可以。应对一些简单的业务场景继承也就够了,但是…...
【uniapp离线打包】(基于Android studio)
文章目录 uniapp打包官方教程入口一、准备工作(工具三大件)Android Studio版本推荐 二、准备工作(Android壳和uniapp包)导入Android壳生成uniapp包将uniapp包导入android壳降低jdk版本 三、准备工作(证书)准备Android平台离线签名…...
稳稳的年化10%,多任务时序动量策略——基于pytorch的深度学习策略(附python代码)
原创文章第608篇,专注“AI量化投资、世界运行的规律、个人成长与财富自由"。 做因子挖掘这段时间,有一个观感。 传统的因子挖掘,尤其是手工构造因子,到遗传算法因子挖掘。——本身也是一种”拟合“,或者说试图”…...
C++分析AVL树
目录 AVL树介绍 AVL树平衡因子更新分析 AVL树插入时旋转与平衡因子更新 左单旋 右单旋 左右单旋 右左单旋 AVL旋转可行性 AVL树节点删除(待补充) AVL树分析 AVL树介绍 二叉搜索树在某些极端情况下可能会退化,为了解决这个问题&…...
aurora8b10b ip的使用(framing接口下的数据回环测试)
文章目录 一、Aurora8B/10B协议二、时钟、复位与状态指示1、时钟2、复位3、状态指示 三、数据发送、接受接口(1)AXI4-Stream位排序(2)Streaming接口(3)Framing接口(帧传输接口) 四、…...
如何通过OpenCV判断图片是否包含在视频内?
要判断图片是否包含在视频内,可以使用计算机视觉技术和图像处理方法。这通常涉及特征匹配或模板匹配。以下是一个基于OpenCV的解决方案,通过特征匹配的方法来实现这一目标。 步骤概述 读取视频和图片: 使用OpenCV读取视频文件和图片文件。 …...
大数据基础:Spark重要知识汇总
文章目录 Spark重要知识汇总 一、Spark 是什么 二、Spark 四大特点 三、Spark框架模块介绍 3.1、Spark Core的RDD详解 3.1.1、什么是RDD 3.1.2、RDD是怎么理解的 四、Spark 运行模式 4.1、Spark本地模式介绍 4.2、Spark集群模式 Standalone 4.3、Spark集群模式 Stan…...
Executable Code Actions Elicit Better LLM Agents
Executable Code Actions Elicit Better LLM Agents Github: https://github.com/xingyaoww/code-act 一、动机 大语言模型展现出很强的推理能力。但是现如今大模型作为Agent的时候,在执行Action时依然还是通过text-based(文本模态)后者JSO…...
循环结构(三)——do-while语句
目录 🍁引言 🍁一、语句格式 🚀格式1 🚀格式2 🍁二、语句执行过程 🍁三、实例 🚀【例1】 🚀【例2】 🚀【例3】 🍁总结 🍁备注 &am…...
pandas 或筛选
pandas 或筛选 在Pandas中,可以使用DataFrame.loc方法结合逻辑运算符来实现或筛选。这里提供一个简单的例子: import pandas as pd 创建示例DataFrame df pd.DataFrame({ ‘A’: [1, 2, 3, 4], ‘B’: [5, 6, 7, 8], ‘C’: [9, 10, 11, 12] }) 设定…...
工具(1)—截屏和贴图工具snipaste
演示和写代码文档的时候,总是需要用到截图。在之前的流程里面,一般是打开WX或者QQ,找到截图工具。但是尴尬的是,有时候,微信没登录,而你这个时候就在写文档。为了截个图,还需要启动微信…...
【从零开始一步步学习VSOA开发】快速体验SylixOS
快速体验SylixOS 安装完毕RealEvo-IDE 后,同时也安装了RealEvo-Simulator。RealEvo-Simulator 是一个虚拟运行环境,可以模拟各种体系结构并在其上运行 SylixOS。相比于物理板卡,在 RealEvo-Simulator 进行运行调测更加的方便快捷且成本低廉。…...
Ansible自动化:简化IT基础设施管理的艺术
目录 一.前言 二.Ansible简介 2.1什么是Ansible? 2.2Ansible的主要特点 2.3Ansible的应用场景 三.探索Ansible的高级功能 3.1 高级Playbook特性 3.2 Ansible Vault 3.3 动态Inventory 3.4Ansible Tower(AWX) 3.5模块开发 3.6 Ans…...
【Rust光年纪】探索Rust语言中的WebSocket库和框架:优劣一览
Rust语言中的实时通信利器:WebSocket库与框架全面解析 前言 随着Rust语言的不断发展,其在Web开发领域也变得越来越受欢迎。WebSocket作为实现实时通信的重要技术,在Rust的生态系统中也有多个库和框架提供了支持。本文将介绍几个主流的Rust …...
HTML 基础结构
目录 1. 文档声明 2. 根标签 3. 头部元素 4. 主题元素 5. 注释 6. 演示 1. 文档声明 <!DOCTYPE html>:声明文档类型,表示该文档是 html 文档, 2. 根标签 (1)所有的其他标签都要放在一对根标签中&#…...
多页合同怎么盖骑缝章_电子合同怎么盖骑缝章?
多页合同怎么盖骑缝章?电子合同怎么盖骑缝章? 对于纸质多页合同,盖骑缝章是一种常见的做法,用于确保合同的完整性,防止任何页面被替换或篡改。以下是盖骑缝章的基本步骤: 将所有合同页面平铺在桌面上。用…...
GD 32 IIC通信协议
前言: ... 通信方式 通信方式分为串行通信和并行通信。常见的串口就是串行通信的方式 常用的串行通信接口 常用的串行通信方式有USART,IIC,USB,CAN总线 同步与异步 同步通信:IIC是同步通信,有两个线一个是时钟信号线,一个数数据…...
Spring Task初学
介绍 Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑 为什么要在Java程序中使用Spring Task? 运行效果 cron表达式:一般日和周不会同时出现 入门案例 启动类添加注解EnableScheduling开始任务调度 创建MyTask类…...
决策树可解释性分析
决策树可解释性分析 决策树是一种广泛使用的机器学习算法,以其直观的结构和可解释性而闻名。在许多应用场景中,尤其是金融、医疗等领域,模型的可解释性至关重要。本文将从决策路径、节点信息、特征重要性等多个方面分析决策树的可解释性&…...
BUGKU-WEB never_give_up
解题思路 F12查看请求和响应,查找线索 相关工具 base64解码URL解码Burp Suit抓包 页面源码提示 <!--1p.html--> 2. 去访问这个文件,发现直接跳转到BUGKU首页,有猫腻那就下载看看这个文件内容吧 爬虫下载这个文件 import requests …...
hive自动安装脚本
使用该脚本注意事项 安装hive之前确定机子有网络。或者yum 更改为本地源,因为会使用epel仓库下载一个pv的软件使用该脚本前提是自行安装好mysql数据库准备好tomcat软件包,该脚本使用tomcat9.x版本测试过能正常执行安装成功,其他版本没有测试…...
unix 用户态 内核态
在UNIX操作系统中,"用户态"和"内核态"是两种不同的运行模式,它们定义了程序在执行时的权限级别: 用户态(User Mode): 用户态是程序运行的常规状态,大多数应用程序在执行时…...
GD32 IAP升级——boot和app相互切换
GD32 IAP升级——boot和app相互切换 目录 GD32 IAP升级——boot和app相互切换1 Keil工程设置1.1 修改ROM1.2 Keil烧录配置 2 代码编写2.1 app跳转2.2 软件重启2.3 app中断向量表偏移 结束语 1 Keil工程设置 1.1 修改ROM GD32内部Flash是一整块连续的内存,但是因为…...
C++11革新之旅:探索C++编程的无限可能
C11革新之旅:探索C编程的无限可能 C11,作为C语言的一个重要标准,为C编程带来了革命性的变革。它不仅引入了众多新特性和改进,还极大地增强了C的表达能力、提高了程序的性能和资源利用率。本文将从多个方面深入探讨C11的新特性&am…...
免费自动化AI视频剪辑工具
下载地址:https://pan.quark.cn/s/3c5995da512e FunClip是一款完全开源、本地部署的自动化视频剪辑工具,通过调用阿里巴巴通义实验室开源的FunASR Paraformer系列模型进行视频的语音识别,随后用户可以自由选择识别结果中的文本片段或说话人&a…...
Linux中安装C#的.net,创建运行后端或控制台项目
安装脚本命令: 创建一个sh文件并将该文件更改权限运行 sudo apt update wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb sudo apt-get upd…...
最长上升子序列LIS(一般+优化)
1. 题目 题目链接: B3637 最长上升子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 输入样例: 6 1 2 4 1 3 4 输出样例: 4 说明/提示: 分别取出 1、2、3、4 即可。 2. 具体实现 2.1 一般做法 dp[i]表示第i个位置的…...
【Python系列】Python 协程:并发编程的新篇章
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
详解C/C++输入输出
前言 C/C输入输出很多,在不同的情况会用不同的输入输出,有的题目在输入时可能换一种输入输出就能不会TLE,有的输入可能要循环输入,但是可以换一种输入直接就能把所有数据输入进去。C/C有哪些常用的输入输出,在什么时候…...
AI人工智能开发环境配置
AI人工智能 为什么使用Python来开发AI 人工智能被认为是未来的趋势技术。 已经有了许多应用程序。 因此,许多公司和研究人员都对此感兴趣。 但是这里出现的主要问题是,在哪种编程语言中可以开发这些 AI 应用程序? 有各种编程语言,…...
wordpress自动添加内链/个人网页制作成品欣赏
无论是利用,Python标准库中有一个专门解析这个数据格式的模块就叫做:json模块。一、json格式介绍 JSON格式是一种轻量级别的数据交换格式,容易被人识别和机器用来解析,它的全称叫做 JavaScript Object Notation。 python json模块…...
柑桔种植服务网站开发/宁波网站推广哪家公司好
三种常用的MySQL建表语句 MySQL建表语句是最基础的SQL语句之一,下面就为您介绍最常用的三种MySQL建表语句,如果您对MySQL建表语句方面感兴趣的话,不妨一看。 1、最简单的:CREATE TABLE t1(id int not null,name char(20) ); 2、带…...
wordpress直接密码注册/农大南路网络营销推广优化
问题发生: Java从网络批量读取图片并保存至本网站服务器后再插入文章中 今天转入一篇文章 http://news.qq.com/a/20170605/045860.htm 发现图片未能成功上传 查看源码发现: 因为没有文件类型所以在转存图片的时候出错了 谷歌百度了一下发现解决办法&…...
网站建设方案书安全性/怎么在百度上推广
今天看到一篇文章使用的方法名称是地理探测器,介绍说比常用的回归方法要有优势,查询了一下方法也容易理解,而且确有其优势,关键是作者专门做了一个page介绍他的软件和方法,中英文的介绍文章也都有,真是十分…...
wordpress弹窗/网络营销自学网站
Clean Code代码规范1、有意义的命名1.1、名副其实知道函数发生了什么,传入什么,返回什么1.2、避免误导避免留下隐藏代码本意的错误线索1.3、做有意义的区分废话是另外一种没有意义的区分1.4、使用读得出来的名称读起来像人话1.5、使用可搜索的名称2、类名…...
东昌网站建设公司/桂林网站设计制作
在看漫威系列电影的时候,你是不是经常会对一些角色感到好奇,想知道每个角色的关联关系和出场的事件,但是却无从下手?现在,我们有很好的库来帮助我们实现这些想法了!Marvel Comics API 允许各地的开发人员访…...