「聊设计模式」之代理模式(Proxy)

2023年 9月 22日 45.5k 0

🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!

前言

  代理模式是一种常见的设计模式,它在我们的日常开发中经常使用。在这篇文章中,我们将深入了解代理模式的概念、应用场景以及实现方式,并通过Java代码实现一个简单的代理模式示例。

摘要

  代理模式是一种结构型设计模式,其目的是为其他对象提供一种代理以控制这些对象的访问。代理类与被代理类之间通过接口进行连接。通过代理对象,客户端不需要直接访问被代理对象,从而实现了代码的解耦,增强了系统的可维护性和可扩展性。

代理模式

概念

  代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象是由客户端对象创建的,它代表了客户端对象所需的真实对象,并在必要时对其进行访问控制。代理对象与真实对象具有相同的接口,因此客户端可以不知道它正在使用代理对象而不是真实对象。代理模式常用于需要控制对对象的访问,并提供其他功能,例如远程访问、安全检查、缓存等。

结构

代理模式结构包括以下几个角色:

  • 抽象主题(Subject):定义主题接口,代理和真实主题实现该接口,保证代理和真实主题有相同的方法。
  • 真实主题(RealSubject):定义真实对象,实现抽象主题接口,是代理角色所代表的真实对象,在客户端直接调用。
  • 代理(Proxy):保持一个引用使得代理可以访问实体,并提供一个与真实主题接口相同的接口,以便可以替代实体。
  • 客户端(Client):使用代理模式时,调用方通过代理对象间接访问真实主题。
  • 如下是代理模式的UML类图:
    在这里插入图片描述

    优缺点

      代理模式是一种结构型设计模式,该模式通过创建一个代理对象来控制对原始对象的访问,从而间接地访问原始对象。代理模式的优点和缺点如下:

    优点:

    代理模式的优点如下:

  • 代理模式可以隐藏客户端真正调用的对象,从而保护了对象的安全性。

  • 代理模式可以对客户端进行控制访问,包括对对象进行加锁、解锁、记录日志等操作。

  • 代理模式可以实现远程访问,即通过代理对象访问远程对象,从而避免了直接访问远程对象的风险。

  • 代理模式可以实现对象的延迟初始化,在需要时才初始化对象,从而提高系统的性能。

  • 缺点:

    代理模式的缺点如下:

  • 代理模式会增加系统的复杂性,因为需要额外实现代理类。

  • 代理模式会降低系统的性能,因为代理类需要额外的处理,从而增加了系统的开销。

  • 代理模式可能会导致对象的数量增加,因为每个对象都需要一个代理对象,从而增加了系统的内存开销。

  • 代理模式可能会导致系统的耦合度增加,因为代理类需要了解被代理类的接口和实现。

  • 应用场景

    代理模式通常在以下场景中被使用:

    • 远程代理:通过代理类来实现远程方法调用。
    • 虚拟代理:通过代理类来实现延迟加载。
    • 安全代理:通过代理类来控制访问权限,例如在Web开发中的Spring Security框架。
    • 智能代理:通过代理类来实现某些额外的操作,例如在Web开发中的AOP框架。

    实现方式

    代理模式可以分为静态代理和动态代理两种实现方式。

    静态代理

      静态代理是在代码编译期间就已经确定好被代理对象和代理对象的关系。在静态代理中,代理类和被代理类一般会实现同一个接口,代理类中会包含一个被代理对象的实例,通过代理类来调用被代理对象的方法。

    静态代理的优点是在运行时速度较快,缺点是需要为每一个被代理对象写一个代理类,当被代理对象比较多时,代码量会急剧增加,维护成本也会随之增加。

    动态代理

      动态代理是在运行期间动态生成代理对象,相对于静态代理,它具有更高的灵活性。在动态代理中,代理对象并不需要实现被代理对象的接口,而是通过代理类的一个方法动态生成一个代理对象。

      动态代理的优点是可以为多个被代理对象提供代理,代码的复用性更好,缺点是在运行时会有一定的性能损耗。

    模式实现

    下面,我们通过一个简单的示例来演示代理模式的代码实现。

      在这个示例中,我们定义了一个接口Image来表示图片对象,有一个实现类RealImage来实现这个接口,表示真正的图片对象。我们还定义了另一个接口ImageProxy,表示代理对象,用来代理RealImage,并提供额外的一些操作。

    Image接口

    package com.example.javaDesignPattern.proxy;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 16:30
     */
    public interface Image {
        void display();
    }
    

    RealImage类

    package com.example.javaDesignPattern.proxy;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 16:31
     */
    public class RealImage implements Image {
        private String fileName;
    
        public RealImage(String fileName) {
            this.fileName = fileName;
            loadFromDisk(fileName);
        }
    
        @Override
        public void display() {
            System.out.println("Displaying " + fileName);
        }
    
        private void loadFromDisk(String fileName) {
            System.out.println("Loading " + fileName);
        }
    }
    

    ImageProxy接口

    package com.example.javaDesignPattern.proxy;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 16:31
     */
    public interface ImageProxy extends Image {
        void showImageInfo();
    }
    

    ImageProxy实现类

    package com.example.javaDesignPattern.proxy;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 16:31
     */
    public class ProxyImage implements ImageProxy {
        private String fileName;
        private RealImage realImage;
    
        public ProxyImage(String fileName) {
            this.fileName = fileName;
        }
    
        @Override
        public void display() {
            if (realImage == null) {
                realImage = new RealImage(fileName);
            }
            realImage.display();
        }
    
        @Override
        public void showImageInfo() {
            System.out.println("Image file: " + fileName);
        }
    }
    

      在上面的代码中,RealImage是被代理对象,实现了Image接口,代表真正的图片文件,ProxyImage是代理对象,实现了ImageProxy接口,代表代理类。在ProxyImage中,它包含一个RealImage对象的实例,通过代理方法display()调用被代理对象的方法,并提供了额外的一个showImageInfo()方法来展示图片的文件名。

    测试用例

    下面,我们通过一个测试用例来检验代理模式的正确性:

    package com.example.javaDesignPattern.proxy;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/19 16:32
     */
    public class ProxyPatternTest {
    
        public static void main(String[] args) {
            Image image = new ProxyImage("test.jpg");
    
            // 代理对象和被代理对象都会调用display()方法
            image.display();
    
            ImageProxy imageProxy = new ProxyImage("test.jpg");
    
            // 只有代理对象才有showImageInfo()方法
            imageProxy.showImageInfo();
        }
    }
    

      在测试用例中,我们先实例化了一个ProxyImage对象,然后通过调用display()方法来显示图片,此时代理对象的display()方法会调用被代理对象的display()方法。然后,我们再实例化一个ProxyImage对象,通过调用showImageInfo()方法来展示图片的文件名,此时只有代理对象才有showImageInfo()方法。

    测试用例执行结果如下所示;

    在这里插入图片描述

    代码分析

      在上面的代码中,我们定义了一个接口Image来表示图片,其中只有一个方法display(),用来显示图片。我们还定义了RealImage类来实现Image接口,实现了真实的图片对象。然后,我们又定义了另一个接口ImageProxy,表示代理对象,用来代理RealImage,并提供了额外的一些操作。

      在ProxyImage中,它包含一个RealImage对象的实例,通过代理方法display()调用被代理对象的方法,并提供了额外的一个showImageInfo()方法来展示图片的文件名。在display()方法的实现中,会判断realImage是否为空,如果为空则实例化一个RealImage对象,并调用display()方法,否则直接调用realImage对象的display()方法。

    附录源码

      如上涉及代码均已上传同步在GitHub,提供给同学们参考性学习。

    总结

      代理模式是一种常见的设计模式,它在我们的日常开发中经常使用。通过代理对象,客户端不需要直接访问被代理对象,从而实现了代码的解耦,增强了系统的可维护性和可扩展性。代理模式可以分为静态代理和动态代理两种实现方式,具体的选择可以根据实际情况来决定。

      在实际开发中,我们可以将代理模式应用到很多场景中,例如远程调用、延迟加载、访问权限控制等。在编写代理模式代码时,我们需要注意代码的复用性和可维护性,同时考虑代码的性能问题。

    ☀️建议/推荐你

      如果想系统性的全面学习设计模式,建议小伙伴们直接毫无顾忌的关注这个专栏《聊设计模式》,无论你是想提升自己的编程技术,还是渴望更好地理解代码背后的设计思想,本专栏都会为你提供实用的知识和启发,帮助你更好地解决日常开发中的挑战,将代码变得更加优雅、灵活和可维护!

    📣关于我

    我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论