c#设计模式-结构型模式 之装饰者模式

🚀介绍

        在装饰者模式中,装饰者类通常对原始类的功能进行增强或减弱。这种模式是在不必改变原始类的情况下,动态地扩展一个对象的功能。这种类型的设计模式属于结构型模式,因为这种模式涉及到两个类型之间的关系,这两个类型是组合在一起的,这种组合关系通常是通过继承来实现的。

        装饰者模式的主要优点是可以在不修改原始类的情况下,通过使用单个类来包装其对象,动态地扩展一个对象的功能。其主要缺点是装饰者模式会导致设计中出现很多小类,如果过度使用,会使程序变得复杂。

👻装饰( Decorator 模式中的角色
  1. 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

🚀案例

我们使用一个案例来对快餐店的点餐功能进行改进,快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、培根这些配菜,加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦,这时候我们就可以使用装饰者模式,在不改变原始类的情况下,动态扩展对象功能。

🐤首先,创建一个最基本的主食抽象类,定义了价格和类型两个属性,以及获取费用和获取类型两个抽象方法

public abstract class FastFood
{/// <summary>/// 价格/// </summary>public float _price { get; set; }/// <summary>/// 类型/// </summary>public string _desc { get; set; }public FastFood(){}public FastFood(float price, string desc){_price = price;_desc = desc;}/// <summary>/// 获取费用/// </summary>/// <returns></returns>public abstract float GetCost();/// <summary>/// 获取类型/// </summary>/// <returns></returns>public abstract string GetDesc();
}

🐤然后,对于上面的主食抽象类分别增加两个实现类,炒饭和炒面,在这里通过基类的含有(float price, string desc)参数的构造函数分别对价格和类型赋值,如果在此处直接调用了GetDesc,那么此时的炒饭和炒粉是什么都还没加的,因此我们在重写GetDesc加上了"啥都不加"字符

/// <summary>
/// 炒饭
/// </summary>
public class FriedRice : FastFood
{public FriedRice() : base(10, "炒饭"){}public override float GetCost(){return _price;}public override string GetDesc(){return _desc + " 啥都不加";}
}/// <summary>
/// 炒面
/// </summary>
public class FriedNoodles : FastFood
{public FriedNoodles() : base(12, "炒面"){}public override float GetCost(){return _price;}public override string GetDesc(){return _desc + " 啥都不加";}
}

🐤创建一个配料抽象类继承于主食抽象类,并且定义了一个FastFood(主食)类型的属性_fastFood

/// <summary>
/// 配料类
/// </summary>
public abstract class Garnish : FastFood
{public FastFood _fastFood { get; set; }
}

🐤对配料类做两个实现,鸡蛋和培根,通过这两个对象,我们可以动态地给一个FastFood对象添加鸡蛋或培根,并计算出新的价格和描述,这就是装饰者模式的核心思想。

在方法中我们再次重写了获取类型和价格的方法。

GetCost()方法是用来计算总价的,即鸡蛋或培根的价格加上被装饰对象的价格。

GetDesc()方法是用来获取描述的,即鸡蛋或培根的描述加上被装饰对象的描述。

/// <summary>
/// 添加鸡蛋
/// </summary>
public class Egg : Garnish
{public Egg(FastFood fastFood){_fastFood = fastFood;_desc = "鸡蛋";_price = 3;}public override float GetCost(){return _price + _fastFood._price;}public override String GetDesc(){return _desc + _fastFood._desc;}
}/// <summary>
/// 添加培根
/// </summary>
public class Bacon : Garnish
{public Bacon(FastFood fastFood){_fastFood = fastFood;_price = 5;_desc = "培根";}public override float GetCost(){return _price + _fastFood._price;}public override String GetDesc(){return _desc + _fastFood._desc;}
}

🐤测试类

class MyClass
{public static void Main(string[] args){//点一份炒饭FastFood riceFood = new FriedRice();//花费的价格Console.WriteLine(riceFood.GetDesc() + " " + riceFood.GetCost() + "元");//点一份炒面FastFood noodleFood = new FriedNoodles();//花费的价格Console.WriteLine(noodleFood.GetDesc() + " " + noodleFood.GetCost() + "元");//点一份加鸡蛋的炒饭FastFood food1 = new FriedRice();food1 = new Egg(food1);//花费的价格Console.WriteLine(food1.GetDesc() + " " + food1.GetCost() + "元");//点一份加培根的炒面FastFood food2 = new FriedNoodles();food2 = new Bacon(food2);//花费的价格Console.WriteLine(food2.GetDesc() + " " + food2.GetCost() + "元");}
}

🐳运行结果

🚀总结

好处:
  • 饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象 来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

使用场景

当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

不能采用继承的情况主要有两类:

  • 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目爆炸性增长;
  • 第二类是因为类定义不能继承(如final类)

在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

当对象的功能要求可以动态地添加,也可以再动态地撤销时。

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

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

相关文章

Centos7 docker 容器内root身份应用自启动 /usr/sbin/init 问题

Centos7 docker 容器内root身份应用自启动 & /usr/sbin/init 问题 环境&#xff1a;我在一个 docker 容器内手动安装了 mysql、nginx、autotestsystem&#xff08;自己的服务&#xff09;&#xff1b; mysql 和 nginx 都做了服务脚本&#xff1a;mysqld.service、nginx.se…

JS 拖拽事件

1.drag等拖拽事件 拖放是由拖动与释放两部分组成&#xff0c;拖放事件也分为被拖动元素的相关事件&#xff0c;和容器的相关事件。 被拖动元素的相关事件如下所示&#xff1a; 被拖动元素相关事件: 事件描述dragstart用户开始拖动元素时触发drag元素正在拖动时触发dragend用户…

图像处理: 马赛克艺术

马赛克 第一章 马赛克的历史渊源 1.1 马赛克 艺术中的一种表面装饰&#xff0c;由紧密排列的、通常颜色各异的小块材料&#xff08;如石头、矿物、玻璃、瓷砖或贝壳&#xff09;组成。与镶嵌不同的是&#xff0c;镶嵌是将要应用的部件放置在已挖空以容纳设计的表面中&#xff0…

面试记录_

1&#xff1a;面试杉岩数据&#xff08;python开发&#xff09; 1.1.1 选择题 for(int i0;i<n;i){for(int j0;j<n;jji) } }O(n) * (O(0) O(n/1) O(n/2) O(n/3) ... O(n/n)) 在最坏情况下&#xff0c;内部循环的迭代次数为 n/1 n/2 n/3 ... n/n&#xff0c;这是…

select完成服务器并发

服务器 #include <myhead.h>#define PORT 4399 //端口号 #define IP "192.168.0.191"//IP地址//键盘输入事件 int keybord_events(fd_set readfds); //客户端交互事件 int cliRcvSnd_events(int , struct sockaddr_in*, fd_set *, int *); //客户端连接事件 …

计算机网络:连接世界的纽带

计算机网络的基础概念 计算机网络是一组相互连接的计算机&#xff0c;它们通过通信链路和协议进行数据交换和资源共享。以下是一些关键概念&#xff1a; 1. 节点和主机 网络中的计算机设备称为节点&#xff0c;通常是主机或服务器。主机是普通用户或终端设备&#xff0c;而服…

计算机图像处理-中值滤波

非线性滤波 非线性滤波是利用原始图像跟模版之间的一种逻辑关系得到结果&#xff0c;常用的非线性滤波方法有中值滤波和高斯双边滤波&#xff0c;分别对应cv2.medianBlur(src, ksize)方法和cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])方法。 …

20分钟---Vue2->Vue3

Vue官网地址&#xff1a;gVue.js - The Progressive JavaScript Framework | Vue.js 选项式vs组合式 vue的两种风格&#xff0c;搬运官网源码&#xff1a; 选项式 API (Options API)​ 使用选项式 API&#xff0c;我们可以用包含多个选项的对象来描述组件的逻辑&#xff0c…

[题]欧拉函数 #欧拉函数

目录 欧拉函数一、用公式求代码 二、线性筛法求欧拉函数扩展欧拉定理 欧拉函数 AcWing 873. 欧拉函数 一、用公式求 定义&#xff1a;1 ~ N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为ϕ(N)。 怎么求呢&#xff1f;&#xff1f; 有一个公式&#xff1a; N p1a1 X…

MASA MAUI iOS 文件下载与断点续传

文章目录 背景介绍方案及代码1、新建MAUI项目2、建立NSUrlSession会话连接3、使用NSUrlSessionDownloadTask 创建下载任务4、DidWriteData 监听下载5、DidFinishDownloading 完成下载6、CancelDownload (取消/暂停)下载7、ResumeDownload 恢复下载8、杀死进程-恢复下载 效果图总…

Python 小爬虫入门 -- 爬取专栏文章标题保存到 CSV 文件中

爬取专栏文章标题保存到 CSV 文件中目标分析网页代码及理解代码段一代码段二成果展示爬取专栏文章标题保存到 CSV 文件中 目标 从一个网页上抓取数据,并保存到一个 CSV 文件中。 具体是爬取 微机系统与接口上机实验_TD PITE型 专栏里的所有 文章标题 并 保存到 csv 文件 中…

huggingface下载文件,可以看下载进度

1、背景&#xff1a; 想下载huggingface上的模型&#xff0c;有些文件又大有多余&#xff0c;怎么下载呢&#xff1f; 2、方法 1.git clone 全部下载&#xff0c;但是没有进度&#xff0c;不知道什么时候还要等多久 3、终极方案huggingface_hub&#xff0c; 在python用代码下…

less与sass

1.变量&#xff1a; Less: my-color: #ff0000;.container {background-color: my-color; } Sass:$my-color: #ff0000;.container {background-color: $my-color; } 在这点上&#xff0c;Less和Sass的变量概念基本相同&#xff0c;都是以声明的方式存储值&#xff0c;然后在…

GitHub配置SSH key

GitHub配置SSH key Git配置信息并生成密钥 设置用户名和密码 设置用户名 git config --global user.name "用户名" 设置邮箱 git confir --global user.email "邮箱" 生成密钥 ssh-keygen -t rsa -C "邮箱" 查看密钥 到密钥所保存的位置 复…

嵌入式Linux应用开发-第十四章查询方式的按键驱动程序

嵌入式Linux应用开发-第十四章查询方式的按键驱动程序 第十四章 查询方式的按键驱动程序_编写框架14.1 LED驱动回顾14.2 按键驱动编写思路14.3 编程&#xff1a;先写框架14.3.1 把按键的操作抽象出一个button_operations结构体14.3.2 驱动程序的上层&#xff1a;file_operation…

2294. 划分数组使最大差为 K-快速排序

2294. 划分数组使最大差为 K-快速排序 给你一个整数数组 nums 和一个整数 k 。你可以将 nums 划分成一个或多个 子序列 &#xff0c;使 nums 中的每个元素都 恰好 出现在一个子序列中。 在满足每个子序列中最大值和最小值之间的差值最多为 k 的前提下&#xff0c;返回需要划分…

(高阶) Redis 7 第16讲 预热/雪崩/击穿/穿透 缓存篇

面试题 什么是缓存预热/雪崩/击穿/穿透如何做缓存预热如何避免或减少缓存雪崩穿透和击穿的区别?穿透和击穿的解决方案出现缓存不一致时,有哪些修补方案缓存预热 理论 将需要的数据提前加载到缓存中,不需要用户使用的过程中进行数据回写。(比如秒杀活动数据等) 方案 1.…

我们是否真的需要k8s?

文章目录 背景k8s相关的讨论为什么要用k8sk8s带来了什么当前业务使用到k8s的核心优势了吗直接自己买服务器会不会更便宜&#xff1f;其他QA没有人可以说出来为什么一定要用k8s而不是其他的没有人可以解释为什么成本核算困难以及成本这么高的原因没有人给出面向C端&#xff0c;面…

吉力宝:智能科技鞋品牌步力宝引领传统产业创新思维

在现代经济环境下&#xff0c;市场经济下产品的竞争非常的激烈&#xff0c;如果没有营销&#xff0c;产品很可能不被大众认可&#xff0c;酒香也怕巷子深&#xff0c;许多传统产业不得不面临前所未有的挑战。而为了冲出这个“巷子”&#xff0c;许多企业需要采用创新思维&#…

NLP 03(LSTM)

一、LSTM LSTM (Long Short-Term Memory) 也称长短时记忆结构,它是传统RNN的变体,与经典RNN相比&#xff1a; 能够有效捕捉长序列之间的语义关联缓解梯度消失或爆炸现象 LSTM的结构更复杂,它的核心结构可以分为四个部分去解析: 遗忘门、输入门、细胞状态、输出门 LSTM内部结构…