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.xdnf.cn/news/147495.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu 20.04编译GPMP2过程记录

前言 GPMP2是董靖博士等人在16-17年提出的结合GTSAM因子图框架与Gaussian Processes完成motion planning的一项工作。前身源于Barfoot教授的课题组提出的STEAM(Simultaneous Trajectory Estimation and Mapping)问题及其相关工作。在提出董靖博士提出GPMP2后&#xff0c;borgl…

时序分解 | Matlab实现SSA-VMD麻雀算法优化变分模态分解时间序列信号分解

时序分解 | Matlab实现SSA-VMD麻雀算法优化变分模态分解时间序列信号分解 目录 时序分解 | Matlab实现SSA-VMD麻雀算法优化变分模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 SSA-VMD麻雀搜索算法SSA优化VMD变分模态分解 可直接运行 分解效果好…

论文笔记:TMN: Trajectory Matching Networks for PredictingSimilarity

2022 ICDE 1 intro 1.1 背景 轨迹相似度可以划分为&#xff1a; 非学习度量方法 通常是为一两个特定的轨迹距离度量设计的&#xff0c;因此不能与其他度量一起使用通常需要二次时间&#xff08;O(n^2)&#xff09;来计算轨迹之间的精确距离基于学习的度量方法 利用机器学习…

源码编译tcpreplay,及使用方法

编译步骤: 下载源码 解压 ./configure make sudo make install 使用方法: tcpreplay --loop1 --intf1网卡名 -x1 pcap文件名 实测结果: 左边是输入的tcpreplay命令 右边是tcpdump截获的udp包

[MAUI程序设计] 用Handler实现自定义跨平台控件

今天来谈一谈MAUI跨平台技术的核心概念——跨平台控件。 无论是MAUI,Xamarin.Forms还是其它的跨平台技术,他们是多个不同平台功能的抽象层,利用通用的方法实现所谓“一次开发,处处运行”。 跨平台框架需要考虑通用方法在各平台的兼容,但由于各原生平台(官方将原生称为本…

ffmpeg、ffplay在线安装,离线导出整个程序,移植到其他服务器使用(linux系统)

环境说明 以ubuntu系统作为说明 在线安装 下面命令会同时安装ffplay和ffmpeg sudo apt-get install ffmpeg怎么验证安装成功&#xff1f; 输入ffmpeg命令 ffmpeg&#xff0c;如图则说明安装成功 转储可执行程序和依赖的文件 找到安装路径&#xff0c;一般在/usr/bin目录…

C++标准模板(STL)- 类型支持 (std::size_t,std::ptrdiff_t,std::nullptr_t)

对象、引用、函数&#xff08;包括函数模板特化&#xff09;和表达式具有称为类型的性质&#xff0c;它限制了对这些实体所容许的操作&#xff0c;并给原本寻常的位序列提供了语义含义。 附加性基本类型及宏 sizeof 运算符返回的无符号整数类型 std::size_t 定义于头文件 <…

电脑显示系统错误怎么办?

有时我们在开机时会发现电脑无法开机&#xff0c;并显示系统错误&#xff0c;那么这该怎么办呢&#xff1f;下面我们就一起来了解一下。 方法1. 替换SAM文件解决问题 1. 重启电脑并进入安全模式。 Win8/10系统&#xff1a;在启动电脑看到Windows标志时&#xff0c;长按电源键…

机器人中的数值优化(二十)——函数的光滑化技巧

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

大数据Flink(九十五):DML:Window TopN

文章目录 DML:Window TopN DML:Window TopN Window TopN 定义(支持 Streaming):Window TopN 是一种特殊的 TopN,它的返回结果是每一个窗口内的 N 个最小值或者最大值。 应用场景

zemax场曲/畸变图与网格畸变图

网格畸变是XY两个方向上的几何畸变&#xff0c;是不同视场实际像高与近轴像高的偏差。 垂轴放大率在整个视场范围内不能保持常数 当一个有畸变的光学系统对一个方形的网状物体成像时,若δy>0&#xff0c;则主光线的交点高度y比理想像高y低,视场越大&#xff0c;低得越多&a…

Apache Derby的使用

Apache Derby是关系型数据库&#xff0c;可以嵌入式方式运行&#xff0c;也可以独立运行&#xff0c;当使用嵌入式方式运行时常用于单元测试&#xff0c;本篇我们就使用单元测试来探索Apache Derby的使用 一、使用IDEA创建Maven项目 打开IDEA创建Maven项目&#xff0c;这里我…

ESP32设备驱动-OLED-SSD1306(I2C)显示屏驱动

OLED-SSD1306(I2C)显示屏驱动 1、OLED介绍 OLED显示屏是指有机电激发光二极管(OrganicLight-EmittingDiode,OLED)由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一…

Vue3切换路由白屏刷新后才显示页面内容

问题所在&#xff1a; 1.首先检查页面路由以及页面路径配置是否配置错误。 2.如果页面刷新可以出来那证明不是配置的问题&#xff0c;其次检查是否在根组件标签最外层包含了个最大的div盒子包裹内容。&#xff08;一般vue3是可以不使用div盒子包裹的&#xff09; 3.最后如果…

无线WIFI工业路由器可用于楼宇自动化

钡铼4G工业路由器支持BACnet MS/TP协议。BACnet MS/TP协议是一种用于工业自动化的开放式通信协议&#xff0c;被广泛应用于楼宇自动化、照明控制、能源管理等领域。通过钡铼4G工业路由器的支持&#xff0c;可以使设备间实现高速、可靠的数据传输&#xff0c;提高自动化水平。 钡…

ARMday2

1~100累加 代码 .text .globl _start _start:mov r0, #1 fun:cmp r0,#100addls r1,r1,r0addls r0,r0,#1b fun .end运行结果

macOS 14 Sonoma 如何删除不需要的 4k 动态壁纸

概览 在升级到 macOS 14&#xff08;Sonoma&#xff09;之后&#xff0c;小伙伴们惊喜发现  提供了诸多高清&#xff08;4k&#xff09;动态壁纸的支持。 现在&#xff0c;从锁屏到解锁进入桌面动态到静态的切换一气呵成、无比丝滑。 壁纸显现可谓是有了“天水相连为一色&…

list(链表)

文章目录 功能迭代器的分类sort函数&#xff08;排序&#xff09;merage&#xff08;归并&#xff09;unique(去重&#xff09;removesplice&#xff08;转移&#xff09; 功能 这里没有“[]"的实现&#xff1b;原因&#xff1a;实现较麻烦&#xff1b;这里使用迭代器来实…

c语言 - 实现每隔1秒向文件中写入当前系统时间

实现思路 主要是通过库函数和结构体获取当前系统时间&#xff08;年月日和时分秒&#xff09;保存到变量里&#xff0c;然后通过格式化输出函数将当前系统时间输出到文件中去。 但是需要注意的是题目要求每隔 1 s对系统时间进行输出&#xff0c;所以需要加入 sleep()函数进行调…

设计模式10、外观模式Facade

解释说明&#xff1a;外观模式&#xff08;Facade Pattern&#xff09;又称为门面模式&#xff0c;属于结构型模式 Faade 为子系统中的一组接口提供了一个统一的高层接口&#xff0c;该接口使得子系统更加容易使用 外观&#xff08;Facade)角色&#xff1a;为多个子系统对外提供…