设计模式第五讲-装饰器模式和代理模式详解

news2024/11/26 3:51:38

一. 装饰器模式

1. 背景

  在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等。在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰模式来实现。

2. 定义和特点

(1). 定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

(2). 优点:

 A. 采用装饰模式扩展对象的功能比采用继承方式更加灵活。

 B.可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。

(3). 缺点

 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

3. 具体实现

(1). 模式结构

 A. 具体的业务类:实现各种业务

 B. 抽象装饰器类

 C.具体的装饰器类

(2). 使用场景

 比如支付成功后,需要修改数据库订单业务,发短信、发邮件、保存本地日志等,每个业务可以写成一个装饰器类,这样可能动态的扩充新的业务,而不是在回调类中直接顺序写死。

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

C/C++Linux服务器开发高级架构师/C++后台开发架构师免费学习地址

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击领取

(3). 代码实操

支付回调类:

     /// <summary>
    /// 支付回调接口
    /// </summary>
    public interface IPayCallback
    {
        /// <summary>
        /// 回调处理方法
        /// </summary>
        void CallbackHandler();
    }
    /// <summary>
    /// 支付成功回调类
    /// </summary>
    public class PayCallback : IPayCallback
    {
        /// <summary>
        /// 回调处理方法
        /// </summary>
        public void CallbackHandler()
        {

            //1.修改数据库订单业务
            Console.WriteLine($"xxxx订单修改完毕,支持成功");

            //2. 还要执行一些逻辑,比如:发短信、发邮件、日志本地存储
            //常规写法,如下,顺序调用对应方法 (本节通过装饰着模式调用)

            //SendSms();   
            //SendMail();
            //SavePayLogs();
        
        }
    }

各个装饰器:

   /// <summary>
    /// 抽象装饰器类
    /// PS:下面的三个装饰器都需要在构造函数中进行传值赋值, 那么我就把这项操作抽象出来,在父类中实现
    /// 让装饰器继承这个父类抽象装饰器即可哦

    /// </summary>
    public abstract class AbstactPayCallbackDecorator
    {
        protected IPayCallback _payCallback;

        public AbstactPayCallbackDecorator(IPayCallback payCallback)
        {
            this._payCallback = payCallback;
        }
    }
 /// <summary>
    /// 支付回调日志装饰器类
    /// </summary>
    public class LogPayCallbackDecorator : AbstactPayCallbackDecorator,IPayCallback
    {

        //原写法
        //IPayCallback _payCallback;
        //public LogPayCallbackDecorator(IPayCallback payCallback)
        //{
        //    this._payCallback = payCallback;
        //}

         
        //使用抽象装饰器类的写法
        public LogPayCallbackDecorator(IPayCallback payCallback) : base(payCallback)
        {
        }


        public void CallbackHandler()
        {
            //1.调用原有方法
            _payCallback.CallbackHandler();

            //2. 执行自己的业务
            SendMail();
        }


        private void SendMail()
        {
            Console.WriteLine($"日志写入成功");
        }

    }

 /// <summary>
    ///支付回调邮件装饰器
    /// </summary>
    public class MailPayCallbackDecorator : AbstactPayCallbackDecorator, IPayCallback
    {
        //原写法
        //IPayCallback _payCallback;
        //public MailPayCallbackDecorator(IPayCallback payCallback)
        //{
        //    this._payCallback = payCallback;
        //}


        //使用抽象装饰器类的写法
        public MailPayCallbackDecorator(IPayCallback payCallback) : base(payCallback)
        {
        }

        public void CallbackHandler()
        {
            //1.调用原有方法
            _payCallback.CallbackHandler();

            //2. 执行自己的业务
            SendMail();
        }


        private void SendMail()
        {
            Console.WriteLine($"发送邮件成功");
        }
    }

  /// <summary>
    /// 支付回调短信装饰器
    /// </summary>
    public class SmsPayCallbackDecorator : AbstactPayCallbackDecorator, IPayCallback
    {
        //原写法
        //IPayCallback _payCallback;
        //public SmsPayCallbackDecorator(IPayCallback payCallback)
        //{
        //    this._payCallback = payCallback;
        //}


        //使用抽象装饰器类的写法
        public SmsPayCallbackDecorator(IPayCallback payCallback) : base(payCallback)
        {
        }


        public void CallbackHandler()
        {
            //1.调用原有方法
            _payCallback.CallbackHandler();

            //2. 执行自己的业务
            SendMail();
        }


        private void SendMail()
        {
            Console.WriteLine($"发送短信成功");
        }
    }

测试代码:

            {
                //1. 正常支付回调
                IPayCallback myPayCallback = new PayCallback();

                //2. 发送短信装饰器
                IPayCallback smsPayCallbackDecorator = new SmsPayCallbackDecorator(myPayCallback);

                //3. 发送邮件装饰器
                IPayCallback mailPayCallbackDecorator = new MailPayCallbackDecorator(smsPayCallbackDecorator);

                //4. 写入日志装饰器
                IPayCallback logPayCallbackDecorator = new LogPayCallbackDecorator(mailPayCallbackDecorator);

                //最终执行
                Console.WriteLine("开始执行了。。。。。。。");
                logPayCallbackDecorator.CallbackHandler();

            }

运行结果:

4. 适用场景分析

 A. 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。

 B. 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。

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

二. 代理模式

1. 背景

 在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。

 在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大(如视频或大图像等),其下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。

2. 定义和特点

(1). 定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

(2). 优点:

  A. 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;

  B. 代理对象可以扩展目标对象的功能;

  C. 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

(3). 缺点

  A. 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;

  B. 增加了系统的复杂度;

3. 具体实现

(1). 模式结构

 代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问.

 A. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

 B. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

 C. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

结构图如下:

(2). 使用场景

 从远程加载图片,图片很大,每次都重新调用很浪费时间,这个时候可以使用代理类来代替缓存,代理中声明真实类,且真实类创建一次则不再创建。

(3). 代码实操

图片接口和真实图片实现类

    /// <summary>
    /// 图片接口
    /// </summary>
   public interface IPicture
   {
        /// <summary>
        /// 图片显示方法
        /// </summary>
        public void Display();
    }

 /// <summary>
    /// 图片加载显示类
    /// </summary>
    public class RealPicture : IPicture
    {
        private string fileName; // 文件名

        public RealPicture(string fileName)
        {
            this.fileName = fileName;
            loadFromDisk(fileName);
        }

        public void Display()
        {
            Console.WriteLine($"文件名为:{fileName}");
        }

        /// <summary>
        /// 磁盘加载图片
        /// </summary>
        /// <param name="fileName"></param>
        private void loadFromDisk(string fileName)
        {
            Console.WriteLine("正在努力加载中: " + fileName);
        }
    }

代理类

    /// <summary>
    ///  图片加载显示代理类
    /// </summary>
    public class ProxyPicture : IPicture
    {
        private RealPicture realImage;
        private string fileName;

        public ProxyPicture(string fileName)
        {
            this.fileName = fileName;
        }

        public void Display()
        {
            // 1、加载磁盘一次
            if (realImage == null)
            {
                realImage = new RealPicture(fileName);
            }
            // 2、直接显示图片
            realImage.Display();
        }
    }

测试

        //1. 使用普通类实现
        IPicture realPic = new RealPicture("test_10mb.jpg");
        realPic.Display();

        //2.使用代理类实现
       IPicture proxyPic = new ProxyPicture("test_10mb.jpg");
       proxyPic.Display();      

运行结果

4. 适用场景分析

 A. 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。

 B. 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。

 C. 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。

 D. 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。

 E. 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,EF中延迟加载。

原文链接:https://www.cnblogs.com/yaopengfei/p/13462523.html

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

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

相关文章

软件测试工程师需要掌握哪些技能呢?

在谈到软件测试工程师时&#xff0c;许多人还是会想到那些重复使用软件并试图在频繁的操作中发现 BUG的人&#xff0c;也就是人们常说的按照测试规范和测试案例来测试软件&#xff0c;检查软件是否有错误&#xff0c;判断软件是否稳定。但这是一个很老派和错误的观点。 由于以…

持续测试:企业的4项策略

对于旅游业和酒店服务来说&#xff0c;节假日无疑是最繁忙的时期。2022年的节假日历经了多重变化&#xff0c;恶劣的天气以及不可抗力因素影响了许多出行计划&#xff0c;也影响了航空公司的运营。为了确保一个顺利和成功的假期&#xff0c;开发团队必须为意想不到的事情做好准…

openvino yolov5/ssd 实时推流目标检测在html上显示

安装ffmepg并添加到环境变量中&#xff0c;流媒体使用m7s 运行效果 SSD&#xff1a;检测在10ms左右&#xff0c;yolov5在100ms左右 app.py #!/usr/local/bin/python3 # encodin: utf-8import subprocess import threading import time import cv2 import osfrom OpenVinoYoloV…

遥感概念理解(更新中)

目录看一幅波长与光对应的图1、波段2、波段组合3、多波段数据的三种存储方式4、全色5、彩色6、 多光谱7、高光谱看一幅波长与光对应的图 1、波段 波段又称为波谱段或波谱带&#xff0c;在遥感技术中&#xff0c;通常把电磁波谱划分为大大小小的段落&#xff0c;大的成为波段区…

2023年数据安全的下一步是什么?

IT 预算和收入增长领域是每个年度开始时的首要考虑因素&#xff0c;在当前的世界经济状况下更是如此。 IT 部门和数据团队正在寻找确定优先级、维护和构建安全措施的最佳方法&#xff0c;同时又具有成本效益。 这是一个棘手的平衡点&#xff0c;但却是一个重要的平衡点&#…

java ssm自行车在线租赁系统idea

当前自行车在社会上广泛使用,但自行车的短距离仍旧不能完全满足广大用户的需求。自行车在线租赁系统可以为用户提供租赁用车等功能,拥有较好的用户体验.能实时在线租赁提供更加快捷方便的租车方式,解决了常见自行车在线租赁系统较为局限的自行车归还功能。 通过使用本系统&…

C语言学习笔记(九):文件的操作

C文件的知识 什么是文件 操作系统把各种设备都统一作为文件来处理。例如&#xff0c;终端键盘是输入文件&#xff0c;显示屏和打印机是输出文件。 文件一般指存储在外部介质上数据的集合。操作系统是以文件为单位对数据进行管理的 输入输出是数据传送的过程&#xff0c;数据…

【LeetCode】1124. 表现良好的最长时间段

1124. 表现良好的最长时间段 题目描述 给你一份工作时间表 hours&#xff0c;上面记录着某一位员工每天的工作小时数。 我们认为当员工一天中的工作小时数大于 8 小时的时候&#xff0c;那么这一天就是「劳累的一天」。 所谓「表现良好的时间段」&#xff0c;意味在这段时间…

多线程下载工具axel的安装和使用

多线程下载工具axel的安装和使用 Axel是一个轻量级下载程序&#xff0c;它和其他加速器一样&#xff0c;对同一个文件建立多个连接&#xff0c;每个连接下载单独的文件片段以更快地完成下载。 Axel 支持 HTTP、HTTPS、FTP 和 FTPS 协议。它也可以使用多个镜像站点下载单个文件…

Springboot 使用插件 自动生成Mock单元测试 Squaretest

缘起 很多公司对分支单测覆盖率会有一定的要求&#xff0c;比如 单测覆盖率要达到 60% 或者 80%才可以发布。 有时候工期相对紧张&#xff0c;就优先开发功能&#xff0c;测试功能&#xff0c;然后再去补单元测试。 但是编写单元测试又比较浪费时间&#xff0c;有没有能够很大…

Spirng 痛苦源码学习(四)——AOP老大哥

文章目录前言一、探究AOP开始&#xff0c;判断导入的相关组件1.跟踪源码流程二、对切面中的增强方法进行增强1.源码的过程三、使用aop的目标类生成代理对象总结前言 Spring的两大特性&#xff1a;IOC&#xff1b;AOP。本篇仅以跟完Spring AOP相关源码为依据写的总结 一、探究A…

MySQL入门篇-MySQL高级窗口函数简介

备注:测试数据库版本为MySQL 8.0 这个blog我们来聊聊MySQL高级窗口函数 窗口函数在复杂查询以及数据仓库中应用得比较频繁 与sql打交道比较多的技术人员都需要掌握 如需要scott用户下建表及录入数据语句&#xff0c;可参考:scott建表及录入数据sql脚本 分析函数有3个基本组成…

matlab进行双目标定获取双目参数并打印教程

文章目录前言1.打开matlab进行双目标定2.获取想要的参数前言 在相同的标定算法和标定参数下&#xff0c;Python和Matlab的标定精度是相同的。因为标定精度主要取决于标定算法和标定参数的质量&#xff0c;而不是编程语言的选择。 不同的编程语言可能使用不同的库或实现细节&…

Hackergame 2020

3.Hackergame 2020 1.签到 url&#xff1a;http://202.38.93.111:10000/ 打开签到题页面&#xff0c;拖动滑杆&#xff0c;如果将滑杆滑动到最左边&#xff0c;提交 0&#xff0c;那么我们会得到成功的返回&#xff0c;但是没有 flag 尝试手动提交一些非整数的值&#xff0c…

HTML+CSS

HTML技术 什么是HTML Hyper Text Markup Language HTML&#xff1a;超文本标记语言&#xff0c;内部全部是一些不同的标记符号。 通俗的来讲&#xff1a;其实就是“网页”。 HTML 网页 网页的特点&#xff1a; 1、所有的网页都是通过【浏览器】来进行解析的。2、所有的网…

【MySQL】子查询

这里写自定义目录标题子查询1、子查询的基本使用2、 单行子查询2.1、单行比较查询2.2、HAVING 中的子查询2.3、CASE中的子查询3、多行子查询4、相关子查询5、EXISTS 与 NOT EXISTS关键字子查询 子查询指一个查询语句嵌套在另一个查询语句内部的查询&#xff0c;这个特性从MySQ…

算法笔记(四)—— 排序算法总结及链表

排序算法稳定性 值相同的元素排序结束后能否保持相对秩序不变。 冒泡排序具有稳定性&#xff08;相等时不交换&#xff09;。 插入排序具有稳定性。 归并排序具有稳定性&#xff08;merge的时候&#xff0c;相等时先拷贝左边的&#xff0c;小和问题让其丧失了稳定性&#x…

557. 反转字符串中的单词 III

给定一个字符串 s &#xff0c;你需要反转字符串中每个单词的字符顺序&#xff0c;同时仍保留空格和单词的初始顺序。 方法一&#xff1a;使用额外空间 思路与算法 开辟一个新字符串。然后从头到尾遍历原字符串&#xff0c;直到找到空格为止&#xff0c;此时找到了一个单词&a…

Http中你必须知道那点事

1, HTTP 1.1 简介 HTTP概念 HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 数据传输的规则指的是请求数据和响应数据需要按照指定的格式进行传输。如果想知道具体的格式&#xff0c;可以打开浏览器&#xf…

2、线程、块和网格

目录一、线程、块、网格概念二、代码分析2.1 打印第一个线程块的第一线程2.2 打印当前线程块的当前线程2.3 获取当前是第几个线程一、线程、块、网格概念 CUDA的软件架构由网格&#xff08;Grid&#xff09;、线程块&#xff08;Block&#xff09;和线程&#xff08;Thread&am…