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

news2024/9/22 23:18:31

总目录


前言

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


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.coloradmin.cn/o/1976807.html

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

相关文章

学习笔记-优化问题

目录 一、目前的问题 1、axios 2、跨域问题 3. 路由安全 二、解决问题 1. 跨域问题 2. 优化URL devServer 1. 配置 devServer 2. 修改请求路径 3. 重启 vue 4. 测试 5. pathRewrite 6. 重启 7. 测试 3. 优化 res.data 4. 判断状态码 5. 引入axios 1. 创建自…

【C++】2.C++入门(2)

文章目录 6.引用6.1 引用概念6.2 引用特性6.3 使用场景6.4 const引用(常引用)6.5 引用和指针的区别 7.inline7.1inline代码举例:7.2inline代码错误示范7.3实现一个ADD宏函数的常见问题: 8.nullptr 6.引用 6.1 引用概念 引用不是…

Yolov8在RK3588上进行自定义目标检测(三)

参考 Yolov8在RK3588上进行自定义目标检测(一) Yolov8在RK3588上进行自定义目标检测(二) best.onnx转yolov8.rknn onnx转rknn需要用到rknn-toolkit2,这个工具暂时不支持windows,所以我们移步linux,我用的是虚拟机创建的ubuntu20.4的系统&a…

JS+H5美观的带搜索的博客文章列表(可搜索多个参数)

实现 美观的界面(电脑、手机端界面正常使用)多参数搜索(文章标题,文章简介,文章发布时间等)文章链接跳转 效果图 手机端 电脑端 搜索实现 搜索功能实现解释 定义文章数据: 文章数据保存在一个 JavaScri…

评价指标--深度学习

目录 1分类任务1.1 二分类1.1.1 含义介绍1.1.2 指标 1.2多分类 2图像分割2.1 常用指标2.2 具体含义2.3 代码实现 1分类任务 1.1 二分类 混淆矩阵 1.1.1 含义介绍 TP:预测为真所以是Positive,预测结果和真实结果一致所以为TrueTN:预测为假…

【Python 逆向滑块】(实战六)逆向滑块,并实现用Python+Node.js 生成滑块、识别滑块、验证滑块、发送短信

逆向日期:2024.08.04 使用工具:Python,Node.js 本章知识:逆向【NECaptchaValidate】参数并成功发送短信 文章难度:中等(没耐心的请离开) 文章全程已做去敏处理!!&#xf…

【SpringBoot】 定时任务之任务执行和调度及使用指南

【SpringBoot】 定时任务之任务执行和调度及使用指南 Spring框架分别通过TaskExecutor和TaskScheduler接口为任务的异步执行和调度提供了抽象。Spring还提供了支持应用程序服务器环境中的线程池或CommonJ委托的那些接口的实现。最终,在公共接口后面使用这些实现&…

POE服务机器人-快速开始

快速开始 POE与服务机器人部署服务机器人与poe集成迭代你的机器人其他 POE与服务机器人 在本快速入门指南中,我们将使用 Python 构建一个机器人服务器,然后将其与 Poe 集成。一旦您创建了由您的服务器驱动的 Poe 机器人,任何 Poe 用户都可以…

解密XXE漏洞:原理剖析、复现与代码审计实战

在网络安全领域,XML外部实体(XXE)漏洞因其隐蔽性和危害性而备受关注。随着企业对XML技术的广泛应用,XXE漏洞也逐渐成为攻击者们利用的重点目标。一个看似无害的XML文件,可能成为攻击者入侵系统的利器。因此&#xff0c…

R语言统计分析——描述性统计

参考资料&#xff1a;R语言实战【第2版】 1、整体统计 对于R语言基础安装&#xff0c;可以使用summary()函数来获取描述性统计量。summary()函数提供了最小值、最大值、四分位数、中位数和算术平均数&#xff0c;以及因子向量和逻辑向量的频数统计。 myvars<-c("mpg&…

JRT多维取数据三件套

今天补齐DolerData判断数据是否存在的API&#xff0c;即M的$d。 兜兜转转&#xff0c;经过近十年探索&#xff0c;3年的酝酿&#xff0c;10个月的开发&#xff0c;JRT终于集齐多维取数据三件套。分别是$get,$listget,$data。通过多维取数据的支持&#xff0c;JRT特别适合医疗数…

7.怎么配置一个axios来拦截前后端请求

首先创建一个axios.js文件 导入我们所需要的依赖 import axios from "axios"; import Element from element-ui import router from "./router"; 设置请求头和它的类型和地址 注意先注释这个url,还没有解决跨域问题,不然会出现跨域 // axios.defaults.…

6-5 多输入多输出通道

虽然我们在前面描述了构成每个图像的多个通道和多层卷积层。例如彩色图像具有标准的RGB通道来代表红、绿和蓝。 但是到目前为止&#xff0c;我们仅展示了单个输入和单个输出通道的简化例子。 这使得我们可以将输入、卷积核和输出看作二维张量。 当我们添加通道时&#xff0c;我…

搭建高可用OpenStack(Queen版)集群(一)之架构环境准备

一、搭建高可用OpenStack&#xff08;Queen版&#xff09;集群之架构环境准备 一、架构设计 二、初始化基础环境 1、管理节点创建密钥对&#xff08;方便传输数据&#xff09; 所有控制节点操作 # ssh-keygen #一路回车即可 Generating public/private rsa key pair. Enter f…

MTK Android12 分析system_app允许vendor_mtk_audiohal_prop SELinux 权限问题

本文将尝试分析&#xff0c;在开发 Android 12 MTK 平台时遇到了 vendor_mtk_audiohal_prop 属性相关的 SELinux 权限问题。包括如何修改 SELinux 策略以允许 system_app 设置 vendor_mtk_audiohal_prop 属性。 问题描述 希望允许 system_app 设置 vendor_mtk_audiohal_prop 属…

SpringBoot+Vue图书(图书借阅)管理系统-附项目源码与配套文档

摘 要 本论文阐述了一套先进的图书管理系统的设计与实现&#xff0c;该系统采用Java语言&#xff0c;结合现代Web开发框架和技术&#xff0c;旨在为图书馆提供高效、灵活且用户友好的资源管理解决方案。系统利用Spring Boot框架为核心&#xff0c;整合MyBatis ORM工具&#…

基于 systemc-2.3.1的virtual device 接入 qemu-arm

1&#xff0c;下载systemc-2.3.1 下载网址&#xff1a; SystemC Files $ wget https://www.accellera.org/images/downloads/standards/systemc/systemc-2.3.1.tgz 2&#xff0c;编译安装 systemc-2.3.1 tar zxf systemc-2.3.1.tgz cd systemc-2.3.1/ export CXXg mkdir bu…

PS 2024 百种常用插件下载安装教程【免费使用,先到先得】

文章目录 软件介绍软件下载安装步骤 专栏推荐&#xff1a; 超多精品软件&#xff08;持续更新中…&#xff09; 软件推荐&#xff1a; PS 2024 PR 2024 软件介绍 PS常用插件 此软件整合了市面近百款ps处理插件&#xff0c;可实现&#xff1a;一键制作背景&#xff0c;一键抠图…

linux安装docker(实操教程)

一、安装前准备工作 1.查看服务器操作系统版本 2.查看服务器的操作系统内核版本 3.安装依赖包 yum install -y yum-utils device-mapper-persistent-data lvm2如果不是root用户登陆的系统&#xff0c;需要手动输入sudo -i切换到root帐户 4.设置阿里云docker-ce镜像源 yum-c…

美国失业率大幅上升,增加九月份降息利率的可能性

令人失望的是&#xff0c;美国7月份经济增加了11.4万个工作岗位&#xff0c;低于预期的17.5万个和6月的17.9万个。平均小时工资持续下降&#xff0c;但失业率升至4.3%。美元继续走低&#xff0c;美国国债也在下跌&#xff0c;而黄金则获得了提振。 7月份的非农业支付数据令人失…