写点什么

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

作者:C++后台开发
  • 2023-02-14
    湖南
  • 本文字数:4892 字

    阅读完需:约 16 分钟

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

一. 装饰器模式

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/

用户头像

C/C++后台开发技术交流qun:720209036 2022-05-06 加入

还未添加个人简介

评论

发布
暂无评论
设计模式第五讲-装饰器模式和代理模式详解_数据结构_C++后台开发_InfoQ写作社区