• C# 设计模式之代理模式


    总目录


    前言

    其实代理模式,在生活中无处不在;就比如租房,一般都是通过中介或者第三方的App去租房子(此处默认他们都是诚信友好的哈),那么为什么我们不自己找房子呢?租过房子应该都知道,自己找房子太麻烦了,既要找原房东,又要搞合同,出了问题扯皮也扯不清等等一些问题,主要是费时费力。那么中介 和 第三方app 就相当于代理了,直接找他们省时省力,很多事情不用管,都由代理去搞定,我们只需看到满意的房子为止。那么我们接下来看看代码中设计模式是如何实现的吧。


    1 基础介绍

    1. 定义:为其他对象提供一种代理以控制对这个对象的访问。

    2. 代理模式的本质就是在客户端和目标对象之间添加一个中介,中介在原对象基础上包了一层封装,封装的时候可以有选择性,有目的性的实现原对象中功能,避免了客户端和目标对象的直接接触,而是通过中介访问。

    3. 代理模式其实没有什么固定的结构,如果是开发初期有计划的开发代理类,可以分为3个角色:

      • 抽象角色(Subject):它声明了真实对象和代理对象的共同接口,真实角色和代理角色继承自抽象角色。
      • 真实角色(RealSubject):继承自抽象角色,实现其真实的业务操作。
      • 代理角色(Proxy):继承自抽象角色,在角色内部创建真实角色的实例,对其内函数进行加工封装操作,以供客户端使用。
    4. 代理模式按照使用目的可以分为以下几种:

      • 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是本电脑中,也可以在另一台电脑中。最典型的例子就是——客户端调用Web服务或WCF服务。
      • 虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得对象只在需要时才会被真正创建。
      • Copy-on-Write代理:虚拟代理的一种,把复制(或者叫克隆)拖延到只有在客户端需要时,才真正采取行动。
      • 保护(Protect or Access)代理:控制一个对象的访问,可以给不同的用户提供不同级别的使用权限。
      • 防火墙(Firewall)代理:保护目标不让恶意用户接近。
      • 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
      • Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以这些结果。

    那么问题来了,为什么不直接使用原对象,反而多此一举的创建这个第三者出来呢。下面就通过一些例子来具体说明。


    举例说明:

    场景一:使用第三方登录时,比如QQ、微信登录,我们不可能直接引用第三方那边的任何源代码的东西,但可以通过封装第三方暴露出来的api才进行开发。在这个场景里,第三方的api就是原对象,我们做封装的类就是代理类。想要实现登录功能,就可以通过代理类来进行。这种网络通信中封装操作就是远程代理


    场景二:在日常开发中,同一个产品,可能对普通用户只开放一小部分功能,对VIP客户开放大部分功能,对管理者开放所有功能。针对不同权限有针对性的开放不同功能,那我们就可以在原对象基础上做封装来实现代理,封闭原对象所有功能。这个封装的代理类就可以控制这个对象的访问,可以给不同的用户提供不同级别的使用权限。这种有选择的开放就是保护代理


    场景三:大家在下载文件或者加载比较复杂的网站时,总会出现一个进度条或者加载loading,这个进度条或loading就使用的代理模式。这种使用消耗小,速度快的效果来替代表示的就是虚拟代理


    场景四:缓存Redis大家应该都使用过,它的作用就是把多终端频繁需要访问的数据从数据库临时存储在内存中共所有终端使用。这种就是缓冲代理

    2 使用场景

    代理模式的类型较多,它们应用于不同的场合:
    (1)、 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
    (2)、当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
    (3)、当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
    (4)、 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
    (5)、当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。

    3 实现方式

    	 public interface Image
        {
            //加载图片
            void Load();
            //显示图片
            void Dispaly();
        }
    
        public class RealImage : Image
        {
    
            public void Dispaly()
            {
                Console.WriteLine("图片显示中");
            }
    
            public void Load()
            {
                Console.WriteLine("图片加载中");
            }
        }
    
        // 定义一个代理类,这是代理模式的核心
        public class ProxyImage : Image
        {
            private Image _Image;
            private string _fileName;
            public ProxyImage(string fileName)
            {
                _Image = new RealImage();
                _fileName = fileName;
    
            }
            public void Dispaly()
            {
                Console.WriteLine($"{_fileName}图片:开始展示");
                _Image.Dispaly();
                Console.WriteLine($"{_fileName}图片:已经显示");
            }
    
            public void Load()
            {
                Console.WriteLine($"{_fileName}图片:开始加载");
                _Image.Load();
                Console.WriteLine($"{_fileName}图片:加载完成");
    
            }
        }
    

    客户端调用:

            static void Main(string[] args)
            {
                Image image = new ProxyImage("机器猫");
                image.Load();
                image.Dispaly();
    
                Console.ReadKey();
            }
    

    从上面案例中,我们可以看到,通过使用代理类,我们可以避免核心的业务逻辑类有太多的冗余逻辑,让其只关注于业务的实现;同时,代理类也可以辅助核心业务实现类,完成一些日记记录等额外操作,让系统功能更趋完善;代理模式也实现了具体实现类和客户之间的解耦,所有的操作只需要通过统一的代理类来进行就可以了。

    4 优缺点分析

    • 优点

      • 代理模式能够将真正被调用的对象隔离,在一定程度上降低了系统的耦合度;
      • 代理对象在客户端和目标对象之间起到一个中介的作用,这样可以起到对目标对象的保护。
      • 代理对象可以在对目标对象发出请求之前进行一个额外的操作,例如权限检查等。
    • 缺点

      • 可能会降低系统的性能和增加内存消耗,因为需要通过代理对象来访问真实对象。
      • 实现代理类也需要额外的工作,从而增加了系统的实现复杂度。

    结语

    希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。

    世事一场大梦,人生几度秋凉。——苏轼《西江月》


    参考资料:
    c#中代理模式详解
    C#设计模式(13)——代理模式(Proxy Pattern)
    C#设计模式之十二代理模式(Proxy Pattern)【结构型】
    C#设计模式12——代理模式的写法

  • 相关阅读:
    深入理解JavaScript堆栈、事件循环、执行上下文和作用域以及闭包
    吃透Spring源码分析专题
    ptables基本语法使用规则
    常见的内置函数、可迭代对象、迭代器对象、异常捕获、异常捕获的用途、生成器对象、模块、绝对导入与相对导入、包的概念、模块
    CNN发展的主要tag
    23.10.15 《CLR via C#》 笔记6
    商业合作保密协议---技术开发
    resize2fs: New size too large to be expressed in 32 bits
    腾讯二面:为什么不建议在 Docker 中跑 MySQL?
    创建git分支命名原则
  • 原文地址:https://blog.csdn.net/qq_39847278/article/details/140957657