设计模式之代理模式(1)

目录

  • 概述
    • 定义
    • 应用场景
    • 主要角色
    • 类图
  • 详述
    • 基本代码
    • 应用实例
    • 符合的设计原则
  • 总结

概述

定义

    代理模式是一种结构型设计模式,它允许通过一个代理对象来控制对原始对象的访问。代理对象可以在不改变原始对象的情况下,增加一些额外的功能,例如权限验证、缓存等。

应用场景

代理模式常用于以下几种情况:

远程代理:代理对象控制对远程对象的访问,例如远程服务调用。
虚拟代理:代理对象代表了一些昂贵或资源消耗大的对象,延迟加载原始对象。
安全代理:代理对象控制对原始对象的访问权限,例如权限验证。

主要角色

  • 目标接口(Subject Interface):定义了目标对象和代理对象共同实现的接口或抽象类。目标接口规定了客户端可以通过代理对象访问的方法。

  • 目标对象(Real Subject):实际执行业务逻辑的对象,是代理对象所代表的真正对象。目标对象实现了目标接口,代理对象将会委托目标对象执行具体的操作。

  • 代理对象(Proxy):代理对象实现了目标接口,并持有一个对目标对象的引用。代理对象在客户端和目标对象之间起到中介的作用,它可以在调用目标对象之前或之后添加额外的逻辑,以实现对目标对象的控制和管理。

  • 客户端(Client):使用代理对象的对象。客户端通过代理对象来访问目标对象的方法,而无需直接与目标对象交互

    在代理模式中,客户端通过代理对象与目标对象进行交互,代理对象在必要时会进行额外的处理。代理对象可以隐藏目标对象的具体实现细节,提供额外的功能或限制访问权限,从而实现对目标对象的保护和控制。

类图

在这里插入图片描述

详述

基本代码

被代理对象


public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

    创建一个接口,作为代理对象和目标对象共同实现的接口

    创建接口的目的是定义代理对象和目标对象共同实现的契约或协议。这个接口定义了代理对象和目标对象之间的通信规范,规定了代理对象需要实现的方法。

    通过定义一个接口,可以将代理对象和目标对象解耦,使得它们可以独立开发和演化。代理对象和目标对象都实现了相同的接口,这意味着它们具有相同的方法签名和行为,可以互相替换使用。

public interface Subject {
    void doSomething();
}

    创建一个代理对象类,实现目标接口,并持有一个对目标对象的引用

    代理对象充当了一个中间人的角色,在客户端和真正执行任务的目标对象之间进行通信和协调。

    代理对象并不是真正执行任务的人,它只是负责管理和控制对目标对象的访问。代理对象可以在执行任务前后添加额外的逻辑或功能,例如权限验证、缓存、日志记录等。

    被代理对象才是真正执行任务的人,它实现了具体的业务逻辑。代理对象在接收到客户端的请求后,会将任务委派给目标对象(被代理对象)来执行。这样可以将任务的执行与具体的业务逻辑分离开来,使得代理对象可以提供一些额外的服务或控制,同时保持目标对象的独立性和可复用性。

public class ProxySubject implements Subject {
    private RealSubject realSubject;

    @Override
    public void doSomething() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        // 在这里可以继续对 realSubject 进行操作
        realSubject.doSomething();
    }

    // 其他代码...
}

注意:代理类当中,为什么要有一个判空的代码?

    第一、这段代码示例中的判空操作称为“延迟初始化”(Lazy Initialization)。延迟初始化是一种性能优化策略,它推迟了对象的创建直到真正需要该对象时才进行。在代理模式的上下文中,这种方式特别有用,因为它允许系统延迟创建计算成本高或者资源消耗大的对象。比如

  • 节约资源:如果realSubject对象的创建成本很高(例如,需要大量内存或时间),那么只有在实际需要使用realSubject对象时才创建它,可以避免在realSubject尚未被使用时就占用宝贵的系统资源。

  • 提高性能:如果realSubject对象在程序运行期间可能根本不会被用到,那么使用延迟初始化可以提高程序启动速度和运行效率,因为避免了不必要的初始化开销。

    第二、代理类通常负责管理实际对象的生命周期,包括实际对象的创建。判空操作就是代理类确保只在首次需要时创建实际对象的一种方式。这样做的好处是,代理类可以在不影响客户端使用的前提下,控制实际对象的初始化过程。

    第三、在实际应用当中是不应该有判空的,因为实际应用当中是被代理类已经存在的,是应该通过依赖倒置注入进来。
再次,在这个里面,判空除了可能想使用原有的被代理类,还可能防止冲突的发生,比如代理,除了代理方法,还有可能代理属性,那么原有的被代理类当中的属性更改之后,如果不判空再创建一个新的被代理类的对象,就会发生冲突。这个的前提是在一个大类当中,这个被代理类没有被回收掉。


    客户端通过代理对象来请求执行任务,并且代理对象会在必要时将请求传递给目标对象。

public class Client {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.doSomething();
    }
}

应用实例

    业务场景:一个追求者(Pursuit)通过代理(Proxy)向心仪的女孩(SchoolGirl)送礼物。

    IGiveGift 接口:定义了送礼物的行为,包括送洋娃娃(giveDolls)、送鲜花(giveFlowers)和送巧克力(giveChocolate)。

public interface IGiveGift {
    void giveDolls();
    void giveFlowers();
    void giveChocolate();
}

    Pursuit 类:追求者类,实现了 IGiveGift 接口,具体执行送礼物的动作。构造函数需要传入一个 SchoolGirl 对象,表示追求者要送礼物的对象。

public class Pursuit implements IGiveGift {
    private SchoolGirl mm;
    public Pursuit(SchoolGirl mm){
        this.mm=mm;
    }
    public void giveDolls(){
        System.out.println(this.mm.getName()+",你好,送你洋娃娃");
    }
    public void giveFlowers(){
        System.out.println(this.mm.getName()+",你好,送你鲜花");
    }
    public void giveChocolate(){
        System.out.println(this.mm.getName()+",你好,送你巧克力");
    }
}

    Proxy 类:代理类,持有追求者(Pursuit)的引用,并且对外提供与 IGiveGift 接口相同的方法。当调用代理的送礼物方法时,实际上是调用追求者的对应方法。 它的构造函数接收一个 SchoolGirl 对象,并创建一个 Pursuit 对象来初始化追求者。

public class Proxy {
    private Pursuit gg;
    private SchoolGirl mm;
    public Proxy(SchoolGirl mm){//代理认识被追求者
       this.gg=new Pursuit(mm);//代理初始化过程中,实际是追求者初始化的过程

    }
    public void giveDolls(){
        gg.giveDolls();
    }
    public void giveFlowers(){
       gg.giveFlowers();
    }
    public void giveChocolate(){
       gg.giveChocolate();
    }
}

    SchoolGirl 类:被追求的女孩类,拥有名字属性和相应的获取及设置方法。

public class SchoolGirl {
    private String name;
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name=name;
    }
}

    Client ,首先创建了一个 SchoolGirl 对象 girlLili,并设置了名字为“丽丽”。然后创建了一个 Proxy 对象 boyDL,并通过代理对象调用送礼物的方法。客户端不需要知道实际对象(Pursuit 类)的实现细节,只需要与代理对象交互。接触耦合。

public class Client {
    public static void main(String[] args) {
    SchoolGirl girlLili=new SchoolGirl();
        girlLili.setName("丽丽");

        Proxy boyDL=new Proxy(girlLili);
        boyDL.giveDolls();
        boyDL.giveChocolate();
        boyDL.giveFlowers();
    }
}

符合的设计原则

  • 单一职责原则(Single Responsibility Principle):一个类应该只有一个引起变化的原因。在代理模式中,代理类(Proxy)负责控制对实际对象的访问,而实际对象(如Pursuit类)则专注于执行其核心业务逻辑。

  • 开闭原则(Open/Closed Principle):软件实体应当对扩展开放,对修改关闭。代理模式允许在不修改实际对象代码的情况下,通过代理类来扩展功能。例如,可以添加新的代理类来实现不同的访问控制策略。

  • 接口隔离原则(Interface Segregation Principle):客户端不应该依赖它不需要的接口。在代理模式中,代理类和实际对象都实现相同的接口(IGiveGift),客户端仅与该接口进行交互,而不是直接与实现细节打交道。

  • 依赖倒转原则(Dependency Inversion Principle):高层模块不应该依赖低层模块,两者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。在代理模式中,客户端代码(Client)依赖于接口(IGiveGift),而不是具体的类(Pursuit或Proxy),这样就可以灵活地替换或修改具体的实现而不影响客户端。

  • 合成复用原则(Composite Reuse Principle):尽量使用对象组合,而不是继承来达到复用的目的。代理模式中,代理类通过包含一个实际对象的引用来实现功能,而不是通过继承实际对象来扩展功能。

  • 最少知识原则(Least Knowledge Principle)或迪米特法则(Law of Demeter):一个对象应该对其他对象有尽可能少的了解。在代理模式中,客户端不需要知道实际对象如何实现或者如何被访问的细节,它只需要与代理对象交互,从而减少了系统中各部分之间的耦合。

总结

    代理模式是一种常用的设计模式,它通过代理对象在保护和控制原始对象访问上起到中间层的作用。今天只讲了静态代理,也就是在编译时就确定了代理对象和原始对象的关系,下次会接着讲动态代理,可以在运行时动态生成代理对象,还有JDK动态代理和CGLIB动态代理的区别。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

差分基准站

差分基准站,又称参考接收机,是一种固定式卫星接收机,用于提高卫星定位精度。 差分基准站的作用是提供已知位置和准确的位置信号,以纠正其他移动定位终端接收器接收到的卫星信号中的误差。 卫星定位信号会受到多种因素的影响&#…

selenium自动化测试:xpath八种定位方式!

01、前言 如果可以的话,请先关注(专栏和账号),然后点赞和收藏,最后学习和进步。你的支持是我继续写下去的最大动力,个人定当倾囊而送,不负众望。谢谢!!! 1.…

【蓝桥杯省赛真题49】Scratch小狗避障 蓝桥杯scratch图形化编程 中小学生蓝桥杯省赛真题讲解

目录 scratch小狗避障 一、题目要求 编程实现 二、案例分析 1、角色分析

JDK安装太麻烦?一篇文章搞定

JDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVMJava系统类库)和JAVA工具。 JDK包含的基本组件包括: javac – 编译器&#xf…

从零开始入门Zapier:与ChatGPT双剑合璧,手把手教程让你进入AI与自动化新纪元

coments 1. 1. 打开Zapier的官方界面 登录之后,会出现一个调查表,可以根据自己的情况进行选择。 第一次注册成功,会送你14天的免费体验

SAP ABAP ALV创建动态树形菜单

创建动态树形菜单——ALV 创建的合同越多,使用树形菜单能比较直观的地显示,而且展开下阶也能明确的知识相关的信息,比如合同中的出口成品有哪些。 设计要点: 第一、 Node_key一定要区分,不能重复,否则出错…

C语言--每日选择题--Day35

第一题 1. 有如下定义:(x y) % 2 (int) a / (int) b 的值是() int x 3; int y 2;float a 2.5; float b 3.5; A:0 B:2 C:1.5 D:1 答案及解析 D 本题是考查强制类型转换和操作符优先级 操作…

JavaWeb 前端工程化

前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低成本。 前端工程化实现技术栈 前端工程化实现的技术栈有很多,我们采用ES6nodejsnpmViteVUE3routerpiniaaxiosElement-plus组合来实现 ECMAScri…

由11月27日滴滴崩溃到近两个月国内互联网产品接二连三崩溃引发的感想

文章目录 知乎文分析微信聊天截图微信公众号 滴滴技术 发文k8s 官方文档滴滴官方微博账号 近两个月国内互联网产品“崩溃”事件2023-10-23 语雀崩溃2023-11-12 阿里云崩溃2023-11-27 滴滴崩溃2023-12-03 腾讯视频崩溃总结 我的感想 知乎文分析 最近连续加班,打车较…

简单的界面与数据分离的架构

草图绘制于2021年2月19日 当时用到了:qt的子项目、delegate、view和widget的关系,有感而写的小备忘,2022年底考的软件设计师里面的设计模式虽然可能早已包含,但自己也得有自己啊,要把自己哪怕不成熟的东西也记录下来&…

操作系统概述及发展史、Linux内核、发行版及应用领域

一、 操作系统(Operation System,OS) 裸机:没有安装操作系统的计算机 如果想在 裸机 上运行自己所编写的程序,就必须用机器语言书写程序如果计算机上安装了操作系统,就可以在操作系统上安装支持的高级语言…

【算法刷题】Day12

文章目录 1004. 最大连续1的个数 III题干:算法原理:1、暴力枚举 计数器2、利用滑动窗口 代码: 746. 使用最小花费爬楼梯题干:算法原理:解法一:1.1 状态表示1.2 状态转移方程1.3 初始化1.4 填表顺序1.5 返回…

unity学习笔记18

模型文件属性简介 1.动画类型:一共有四种:无 表示没有动画,旧版 就表示这个模型文件里面的动画片段可以用animation组件来播放的,最后两个 ”泛型“和“人形”都是animator组件来播放的。区别是泛型支持所有类型的动画播放&#x…

LangChain的函数,工具和代理(四):使用 OpenAI 函数进行标记(Tagging) 提取(Extraction)

在上一篇博客LangChain中轻松实现OpenAI函数调用 中我们学习了如何使用Pydantic来生成openai的函数描述对象,并且通过在langchain中调用Pydantic生成的函数描述变量来轻松实现openai的函数调用功能,在此基础上今天我们再介绍两个非常实用的功能&#xff…

vue实现css过渡与css动画

一、过渡和动画的区别 过渡:通常用来表示元素上属性状态的变化。动画:通常用来表示元素运动的情况。 二、使用Vue实现基础得css过渡与动画 1. 动画 /* css */ keyframes leftToRight {0% {transform: translateX(-100px);}50% {transform: translateX(-5…

万兆多模光模块SFP-10G-SR:高速短距传输的最优选

随着信息技术的发展,企业和个人对数据传输速度和带宽需求不断增加。传统的千兆以太网已经不能满足高速数据传输的要求,因此万兆以太网技术崭露头角。作为万兆以太网中的重要组件之一,万兆多模SFP-10G-SR光模块引起了广泛的关注。本文将介绍万…

Sentinel基础知识

Sentinel基础知识 资源 1、官方网址:https://sentinelguard.io/zh-cn/ 2、os-china: https://www.oschina.net/p/sentinel?hmsraladdin1e1 3、github: https://github.com/alibaba/Sentinel 一、软件简介 Sentinel 是面向分布式服务架构的高可用流量防护组件…

【原神游戏开发日志1】缘起

【原神游戏开发日志1】缘起 版权声明 本文为“优梦创客”原创文章,您可以自由转载,但必须加入完整的版权声明 文章内容不得删减、修改、演绎 相关学习资源见文末 大家好,最近看到原神在TGA上频频获奖,作为一个14年经验的游戏开…

springboot集成docker

1、快速构建springboot-demo项目 地址:https://start.spring.io/

【C++】异常处理 ⑧ ( 标准异常类 | 标准异常类继承结构 | 常用的标准异常类 | 自定义异常类继承 std::exception 基类 )

文章目录 一、抛出 / 捕获 多个类型异常对象1、标准异常类2、标准异常类继承结构3、常用的标准异常类 二、自定义异常类继承 std::exception 基类1、自定义异常类继承 std::exception 基类2、完整代码示例 - 自定义异常类继承 std::exception 基类 一、抛出 / 捕获 多个类型异常…