C# 设计模式之装饰器模式

总目录


前言

装饰器模式的主要作用就是扩展一个类的功能,或给一个类添加多个变化的情况。学习面向对象的都知道,如果想单纯的给某个类增加一些功能,可以直接继承该类生成一个子类就可以。应对一些简单的业务场景继承也就够了,但是面对一些复杂的业务场景,仅靠继承是不够的。那么我们看看装饰器模式是如果以一个更为灵活的方式扩展一个对象的功能的。


1 基础介绍

  1. 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更为灵活。
  2. 适用于需要扩展一个类的功能,或给一个类添加多个变化的情况。
  3. 在装饰模式中的角色:
    • 抽象构件角色(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)

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

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

相关文章

共享`pexlinux`数据文件的网络服务

实验环境准备: 1.红帽7主机 2.要全图形安装 3.配置网络为手动,配置网络可用 4.关闭vmware DHCP功能 一、kickstart自动安装脚本制作 1.安装图形化生成kickstart自动脚本安装工具 2.启动图形制作工具 3.图形配置脚本 这里使用的共享方式是http&#xff0…

2024靠这份软件测试面试题宝典已成功上岸,跳槽成功

上月很多朋友靠这份面试宝典拿到大厂的office,跳槽成功,面试找工作的小白和要跳槽进阶都很适合,没有一点准备怎么能上岸成功呢? 这份面试题宝库,包含了很多部分:测试理论,Linux基础&#xff0c…

每日OJ_牛客WY15 幸运的袋子

目录 牛客HJ62 查找输入整数二进制中1的个数 解析代码 牛客HJ62 查找输入整数二进制中1的个数 查找输入整数二进制中1的个数_牛客题霸_牛客网 解析代码 本题是计算一个数二进制表示中1的个数,通过(n >> i) & 1可以获取第i位的二进制值&…

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…

【文件系统】抽象磁盘的存储结构 CHS寻址法 | sector数组 | LAB数组

目录 1.为什么要抽象 2.逻辑抽象_版本1 2.1sector数组 ​2.2index转化CHS 3.逻辑抽象_版本2 3.1LBA数组 3.2LAB下标转化sector下标 文件其实就是在磁盘中占有几个扇区的问题❗文件是很多个sector的数组下标❗文件是有很多块构成的❗❗文件由很多扇区构成------>文件…

PXE+Kickstart自动化安装RHEL7.9

准备环境 1. 一台RHEL7.9主机 2. 开启主机图形 如果是7.9的主机是图形化界面了 就输入命令init 5 开启图形 如果主机一开始没装图形化界面,可以使用以下命令安装 # yum group install "Server with GUI" -y 3. 配置网络可用 4. 关闭VMware dhcp功…

音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现

音视频入门基础:WAV专题系列文章: 音视频入门基础:WAV专题(1)——使用FFmpeg命令生成WAV音频文件 音视频入门基础:WAV专题(2)——WAV格式简介 音视频入门基础:WAV专题…

IJCAI 2024 | 时空数据(Spatial-Temporal)论文总结

2024 IJCAI(International Joint Conference on Artificial Intelligence, 国际人工智能联合会议)在2024年8月3日-9日在韩国济州岛举行。 本文总结了IJCAI2024有关时空数据(Spatial-temporal) 的相关论文,如有疏漏,欢迎大家补充。…

给水排水杂志

一、基本信息 《给水排水》创刊于1964年,是国内创刊早、发行量大、内容涵盖广的水行业权威期刊,在业内享有盛誉。期刊现由中国建设集团股份有限公司主管,亚太建设科技信息研究院有限公司、中国土木工程学会主办。现任名誉主编&#xff1a…

SDXL总结

SDXL base部分的权重:https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/tree/main diffusers库中的SDXL代码pipelines: https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/stable_diffusion_xl 参考&…

目标跟踪那些事

目标跟踪那些事 跟踪与检测的区别 目标跟踪和目标检测是计算机视觉中的两个重要概念,但它们的目的和方法是不同的。 目标检测(object Detection):是指在图像或视频帧中识别并定位一个或多个感兴趣的目标对象的过程 。 目标跟踪(object Tracking)&…

力扣爆刷第169天之TOP200五连刷111-115(课程表、单词搜索、归并)

力扣爆刷第169天之TOP200五连刷111-115(课程表、单词搜索、归并) 文章目录 力扣爆刷第169天之TOP200五连刷111-115(课程表、单词搜索、归并)一、207. 课程表二、LCR 125. 图书整理 II三、402. 移掉 K 位数字四、79. 单词搜索五、9…

设计模式:详细拆解策略模式

策略模式 既然是详解,就不以案例开头了,直奔主题,先来看看什么是策略模式。 模式定义 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式 使得算法可独立于使用它的客户而变化。 结构 Strategy&a…

C++ | Leetcode C++题解之第318题最大单词长度乘积

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxProduct(vector<string>& words) {unordered_map<int,int> map;int length words.size();for (int i 0; i < length; i) {int mask 0;string word words[i];int wordLength word.s…

深入解析Java虚拟机(JVM)内存模型-全面掌握JVM内存管理

Java虚拟机(JVM)的内存模型是Java开发者必须掌握的核心知识之一。无论你是刚入门的新手,还是经验丰富的老手,深入理解JVM内存模型都能帮助你写出更高效、更稳定的Java程序。本文将带你全面剖析JVM内存模型的各个组成部分,深入探讨其工作原理,并通过实例讲解如何进行内存优化。让…

C#-读取测序数据的ABI文件并绘制svg格式峰图

本地环境&#xff1a;win10&#xff0c;visual studio 2022 community 目录 前言问题描述实现效果解决思路实现要点ABI文件的组织方式svg绘制问题变色碱基值 动态设置svg图像宽度 前言 本文是在已有的代码基础上进行的开发&#xff0c;前期已经实现&#xff1a; ABI文件的解析…

【从零搭建SpringBoot3.x 项目脚手架】- 1. 工程初始化

为什么会有这个系列文章 在项目开发中&#xff0c;大多项目依旧沿用的是 JDK 8 Spring Boot 2.x 系列的技术栈&#xff0c;没有Spring Boot 3.x 的上手实践机会。在个人学习探索 Spring Boot 3.x 的过程中&#xff0c;遇到多数第三方框架集成和问题排查的技术问题&#xff0c…

[极客大挑战 2019]Secret File-web

打开题目 查看源码 直接访问Archive_room.php 第二个页面是个点击框&#xff0c;这里bp抓包确认&#xff1b;若是直接SECRET&#xff0c;会跳到end.php 直接访问secr3t.php 代码审计一下 playload&#xff1a;secr3t.php?fileflag.php 改为php协议读取权限 secr3t.php?f…

[图解]SysML建模电磁轨道炮-01块定义图

1 00:00:00,490 --> 00:00:04,000 我们是用EA复刻一个网络上的案例 2 00:00:06,370 --> 00:00:09,240 电磁轨道炮&#xff0c;它的原理很简单 3 00:00:09,490 --> 00:00:10,960 初中物理就可以理解 4 00:00:11,670 --> 00:00:14,010 首先&#xff0c;电流形成磁…

polyfit曲线拟合

一、简介 polyfit函数是matlab中用于进行曲线拟合的一个函数。其数学基础是最小二乘法曲线拟合原理。曲线拟合&#xff1a;已知离散点上的数据集&#xff0c;即已知在点集上的函数值&#xff0c;构造一个解析函数&#xff08;其图形为一曲线&#xff09;使在原离散点上尽可能接…