C# 设计模式(结构型模式):装饰器模式

C# 设计模式(结构型模式):装饰器模式

在软件开发中,面对需要扩展功能但又不想修改已有代码的情况时,装饰模式(Decorator Pattern)是一个非常有用的设计模式。装饰模式允许我们在不改变对象自身的情况下,动态地为其添加新的功能。它通过创建一个装饰器类来包裹原始对象,从而增强对象的行为。

1. 装饰模式的定义

装饰模式是一种结构型设计模式,主要用于在不改变对象结构的前提下,动态地为对象添加额外的职责。装饰模式通过创建一个包装类来“装饰”目标对象,并且该包装类能够增强或修改目标对象的行为。

2. 装饰模式的结构

装饰模式通常包括以下几个部分:

  • Component(组件接口):定义了一个基本接口,描述了具体类和装饰器类共同遵循的操作方法。
  • ConcreteComponent(具体组件):实现了组件接口,表示需要被装饰的原始对象。
  • Decorator(装饰器类):实现了组件接口,并持有一个组件实例,在装饰器类中增强或修改该组件的行为。
  • ConcreteDecorator(具体装饰器类):继承自装饰器类,实现对具体功能的扩展。
3. 装饰模式的应用场景

装饰模式适用于以下几种场景:

  • 当你希望在不修改原有代码的情况下,向对象添加额外的功能。
  • 当对象的功能扩展非常复杂,无法通过继承来实现时,使用装饰模式可以动态地组合不同的功能。
  • 当你需要以透明的方式增强对象的功能,且希望增强功能可以在运行时自由选择。
4. C# 实现装饰模式

假设我们有一个简单的饮料类,表示不同类型的饮料。我们希望能够动态地为饮料添加额外的配料(如牛奶、糖等),而不修改原始饮料的代码。我们可以使用装饰模式来实现这一功能。

using System;

// 组件接口
public interface IDrink
{
    string GetDescription();
    double Cost();
}

// 具体组件:咖啡
public class Coffee : IDrink
{
    public string GetDescription()
    {
        return "Coffee";
    }

    public double Cost()
    {
        return 2.0;
    }
}

// 具体组件:茶
public class Tea : IDrink
{
    public string GetDescription()
    {
        return "Tea";
    }

    public double Cost()
    {
        return 1.5;
    }
}

// 装饰器类
public abstract class DrinkDecorator : IDrink
{
    protected IDrink _drink;

    public DrinkDecorator(IDrink drink)
    {
        _drink = drink;
    }

    public virtual string GetDescription()
    {
        return _drink.GetDescription();
    }

    public virtual double Cost()
    {
        return _drink.Cost();
    }
}

// 具体装饰器:添加牛奶
public class MilkDecorator : DrinkDecorator
{
    public MilkDecorator(IDrink drink) : base(drink) { }

    public override string GetDescription()
    {
        return _drink.GetDescription() + " + Milk";
    }

    public override double Cost()
    {
        return _drink.Cost() + 0.5;
    }
}

// 具体装饰器:添加糖
public class SugarDecorator : DrinkDecorator
{
    public SugarDecorator(IDrink drink) : base(drink) { }

    public override string GetDescription()
    {
        return _drink.GetDescription() + " + Sugar";
    }

    public override double Cost()
    {
        return _drink.Cost() + 0.2;
    }
}

// 客户端代码
class Program
{
    static void Main(string[] args)
    {
        // 创建一杯咖啡
        IDrink drink = new Coffee();
        Console.WriteLine($"{drink.GetDescription()} costs {drink.Cost()}");

        // 给咖啡添加牛奶
        drink = new MilkDecorator(drink);
        Console.WriteLine($"{drink.GetDescription()} costs {drink.Cost()}");

        // 给咖啡添加糖
        drink = new SugarDecorator(drink);
        Console.WriteLine($"{drink.GetDescription()} costs {drink.Cost()}");

        // 创建一杯茶
        IDrink tea = new Tea();
        tea = new MilkDecorator(tea);  // 给茶添加牛奶
        Console.WriteLine($"{tea.GetDescription()} costs {tea.Cost()}");
    }
}

在这个例子中:

  • IDrink 是组件接口,定义了饮料的基本方法 GetDescriptionCost
  • CoffeeTea 是具体的组件,分别表示咖啡和茶。
  • DrinkDecorator 是装饰器类,它实现了 IDrink 接口,并持有一个 IDrink 实例,允许我们为饮料添加额外的功能。
  • MilkDecoratorSugarDecorator 是具体的装饰器类,分别为饮料添加牛奶和糖。

通过装饰模式,我们可以动态地为饮料对象添加不同的配料,且不需要修改原有的 CoffeeTea 类。每次增强功能时,我们只需要创建一个新的装饰器类,并将其包装在原始对象上。

5. 装饰模式的优缺点

优点

  • 灵活性高:通过装饰器,可以在不修改原始类的情况下动态地增加功能。
  • 避免类爆炸:通过组合装饰器类来扩展功能,避免了继承层次的膨胀。
  • 增强了对象的功能:可以轻松地为对象添加额外的功能,而不会影响其他对象。

缺点

  • 增加了系统复杂性:装饰器模式可能导致系统中有多个装饰器类,使得代码结构复杂。
  • 过度装饰问题:如果装饰器过多,可能会导致调试和理解代码变得困难。
6. 总结

装饰模式是一个非常灵活的设计模式,适用于需要在运行时为对象动态添加功能的场景。它通过装饰器类来增强目标对象的行为,并允许我们不修改原始对象的代码就能实现功能扩展。掌握装饰模式,可以帮助我们设计出更加灵活、可扩展的系统,尤其适用于那些具有很多扩展功能的对象。


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何轻松关闭 iPhone 上的 HEIC [HEIC 图像技巧]

您是否正在为关闭 iPhone 上的 HEIC 而烦恼?你不是一个人; Apple 的首选图像文件格式仍可能存在一些兼容性问题。当您与某人共享照片或尝试在Windows计算机上打开图像时,就会出现此问题。幸运的是,Apple 使关闭 HEIC iPhone 变得更加容易。 …

GRU-PFG:利用图神经网络从股票因子中提取股票间相关性

“MCI-GRU: Stock Prediction Model Based on Multi-Head Cross-Attention and Improved GRU” 论文地址:https://arxiv.org/pdf/2410.20679 摘要 金融市场因复杂性及大数据时代的来临,使得准确预测股票走势变得尤为重要。传统的时序分析模型&#xff0…

UE5失真材质

渐变材质函数:RadialGradientExponential(指数径向渐变) 函数使用 UV 通道 0 来产生径向渐变,同时允许用户调整半径和中心点偏移。 用于控制渐变所在的位置及其涵盖 0-1 空间的程度。 基于 0-1 的渐变中心位置偏移。 源自中心的径…

Go语言在实际项目中的应用:从RESTful API到日志监控 (十四)

Go语言在实际项目中的应用:从RESTful API到日志监控 🚀 Go语言(又叫Golang)作为一种现代化的编程语言,凭借其简洁的语法和强大的性能,已经成为了很多企业技术栈的一部分。在实际项目中,Go不仅仅…

3blue1brow线代笔记

向量 物理:空间中的箭头,长度和方向决定一个向量。只要两者相同,可以任意移动保持不变 计算机:有序的数字列表 (数组) 数学:向量可以是任何东西,只要保证两个向量相加以及数字与向量…

壁纸样机神器,这个工具适合专业设计师用吗?

壁纸样机神器在一定程度上适合专业设计师使用,但是否适合具体取决于设计师的需求和使用场景: 适合专业设计师的方面 快速实现设计想法:专业设计师在创作过程中,有时需要快速将设计想法变为可视化的效果图,以便进行初…

STM32CUBEIDE FreeRTOS操作教程(十二):std dynamic memory 标准动态内存

STM32CUBEIDE FreeRTOS操作教程(十二):std dynamic memory 标准动态内存 STM32CUBE开发环境集成了STM32 HAL库进行FreeRTOS配置和开发的组件,不需要用户自己进行FreeRTOS的移植。这里介绍最简化的用户操作类应用教程。以STM32F40…

在线机考|2024华为实习秋招春招编程题(最新)——第3题_PCB印刷电路板布线_300分(八)

题目内容 在PCB印刷电路板设计中,器件之间的连线需要避免线路的阻抗值增大、而且赛件之间还有别的器件和别的干扰源,在布线时我们希望受到的干扰尽量小。现将电路板简化成一个MN的矩阵,每个位置(单元格)的值表示其源干扰度。 如果单元格的值为0,表示此位置没有干扰源;如果单…

1961-2022年中国大陆多干旱指数数据集(SPI/SPEI/EDDI/PDSI/SC-PDSI/VPD)

DOI: 10.5194/essd-2024-270 干旱指数对于评估和管理缺水和农业风险至关重要;然而,现有数据集中缺乏统一的数据基础,导致不一致,对干旱指数的可比性提出了挑战。本研究致力于创建CHM_Drought,这是一个创新且全面的长期气象干旱数…

建造者模式 Builder Pattern

在创建一个对象的时候,构造器参数有点多,而且有些参数还是可选的,再者还有不少同类型的,那就更应该使用 builder 模式了。 使用 Builder 模式的初衷是 把易变性(mutability)移动到Builder类,而…

【人工智能机器学习基础篇】——深入详解监督学习之模型评估:掌握评估指标(准确率、精确率、召回率、F1分数等)和交叉验证技术

深入详解监督学习之模型评估 在监督学习中,模型评估是衡量模型性能的关键步骤。有效的模型评估不仅能帮助我们理解模型在训练数据上的表现,更重要的是评估其在未见数据上的泛化能力。本文将深入探讨监督学习中的模型评估方法,重点介绍评估指…

Linux(Ubuntu24.04)源码编译安装VTK7.1.1记录

VTK(Visualization Toolkit)是一个开源的3D可视化开发工具包,用于开发可视化和图形处理应用程序。VTK提供了一系列的算法和工具,用于创建、渲染和处理复杂的3D图形和数据。VTK由C编写,并提供了Python、Java和Tcl等语言…

FICO财务模块在SAP ECC与S4 HANA系统间的差异有哪些?

【SAP系统研究】 #SAP #FICO #ECC #HANA #Oracle #SAP财务 尽管SAP S4/HANA已经发布很久,但使用SAP ECC系统的企业也仍然很多。 这两个系统在FICO模块中有哪些常见的不同呢? 1、数据库表 ①SAP ECC系统 可以在Oracle、IBM DB2等数据库上运行 ②SAP S…

CDPHudi实战-集成spark

[一]使用Spark-shell 1-配置hudi Jar包 [rootcdp73-1 ~]# for i in $(seq 1 6); do scp /opt/software/hudi-1.0.0/packaging/hudi-spark-bundle/target/hudi-spark3.4-bundle_2.12-1.0.0.jar cdp73-$i:/opt/cloudera/parcels/CDH/lib/spark3/jars/; done hudi-spark3.4-bu…

mac m2 安装 docker

文章目录 安装1.下载安装包2.在downloads中打开3.在启动台打开打开终端验证 修改国内镜像地址小结 安装 1.下载安装包 到官网下载适配的安装包:https://www.docker.com/products/docker-desktop/ 2.在downloads中打开 拖过去 3.在启动台打开 选择推荐设置 …

Power BI如何连接Azure Databricks数据源?

故事背景: 近期有朋友询问,自己公司有一些项目使用了Azure Databricks用于数据存储。如何使用Power BI Desktop桌面开发软件连接Azure Databricks的数据源呢? 解决方案: 其实Power BI是提供了连接Azure Databricks数据源的选项的,只是配置…

Python入门教程 —— 进制转换

找其他编译器,系统解释器,这样速度会快很多。 进制 现代的计算机和依赖计算机的设备里都用到二进制(即0和1)来保存和表示数据,一个二进制表示一个比特(Bit)。 在二进制的基础上,计算机还支持八进制和十六进制这两种进制。 除了…

HTML5新特性|05 CSS3边框CSS3背景

CSS3边框 1、CSS3边框: 通过CSS3,您能够创建圆角边框,向矩形添加阴影,使用图片来绘制边框-并且不需使用设计软件,比如PhotoShop。 属性: border-radius 圆角box-shadow:水平阴影 垂直阴影 阴影的清晰度 阴影的大小 阴影的颜色…

《Vue3实战教程》26:Vue3Transition

如果您有疑问,请观看视频教程《Vue3实战教程》

SpringCloudAlibaba实战入门之Sentinel服务降级和服务熔断(十五)

一、Sentinel概述 1、Sentinel是什么 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 一句话概括:sentinel即Hystrix的替代品,官网: https://sentinelguard.io/zh…