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

目录

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

概述

定义

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

应用场景

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

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

主要角色

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

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

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

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

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

类图

在这里插入图片描述

详述

基本代码

被代理对象


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

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

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

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

public interface Subject {void doSomething();
}

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

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

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

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

public class ProxySubject implements Subject {private RealSubject realSubject;@Overridepublic 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动态代理的区别。

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

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

相关文章

如何做好一个软件开发项目经理?

要成为一名优秀的软件开发项目经理,需要具备一定的技术知识和管理能力。下面是学习和发展软件开发项目经理职业所需的关键能力和工作内容。 首先,作为软件开发项目经理,你需要具备扎实的软件开发知识和技能。这包括熟悉常用的编程语言、开发框…

Python教程75:textwrap模块 — 文本自动换行与填充

TextWrap模块是一个Python模块,它提供了一些函数和方法来处理文本字符串的包装和填充。这个模块的主要功能是将一个长字符串根据指定的宽度分成多行,可以用于格式化文本输出,使其在指定的行数内完成显示。这个过程通常用于控制文本在终端、文…

面向注解编程—Spring 注解看这一篇就够了

面向注解编程—Spring 注解大全之IOC篇 Spring的一个核心功能是IOC,就是将Bean初始化加载到容器中,调用的时候直接注入即可,使用注解可以大大提高了开发效率!。 Bean英文意为 豆子 理解起来就是豆荚里的豌豆,豆荚就是…

.Net core 6.0 升8.0

1 Update Visual Studio 2 3 用Nutget 更新不同套件版本 更新后结果如下:

第四期丨酷雷曼无人机技能培训

第4期无人机技能培训 2023年10月25日,酷雷曼无人机技能培训及执照考试第四期成功举办,自7月份首期开办以来,已按照每月一期的惯例连续举办四期,取得了极为热烈的反响。 随着无人机培训的重要性及影响力逐渐扩大,参加培…

UE4/UE5 材质实现带框环形进度条

UE4/UE5 材质实现带框环形进度条 此处使用版本:UE4.27 原理:大圆减小圆可以得到圆环,大圆环减小圆环,可以得到圆环外围线框 实现效果: 实现(为了给大家放进一张面前能看的图,我费劲了心思&…

从 MQTT、InfluxDB 将数据无缝接入 TDengine,接入功能与 Logstash 类似

利用 TDengine Enterprise 和 TDengine Cloud 的数据接入功能,我们现在能够将 MQTT、InfluxDB 中的数据通过规则无缝转换至 TDengine 中,在降低成本的同时,也为用户的数据转换工作提供了极大的便捷性。由于该功能在实现及使用上与 Logstash 类…

自动化巡检实现方法 (一)------- 思路概述

一、自动化巡检需要会的技能 1、因为巡检要求一天24小时全天在线,因此巡检程序程序一定会放在服务器上跑,所以要对linux操作熟悉哦 2、巡检的代码要在git上管理,所以git的基本操作要熟悉 3、为了更方便不会代码的同学操作,所以整个…

GAN:WGAN-GP-带有梯度惩罚的WGAN

论文:https://arxiv.org/pdf/1704.00028.pdf 代码:GitHub - igul222/improved_wgan_training: Code for reproducing experiments in "Improved Training of Wasserstein GANs" 发表:2017 WGAN三部曲的终章-WGAN-GP 摘要 WGAN在…

123456前端调AES加密方法变为YehdBPev

使用密码加密服务: pig4cloud 加密服务

【每日易题】Leetcode上Hard难度的动态规划题目——地下城游戏的实现

君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们,这里是君兮_,博主最近一直在钻研动态规划算法,最近在Leetcode上刷题的时候遇到一个Hard难度的动态规划题,今天就借此机会来给大家分享…

品牌保护与知识产权:跨境电商中的法律挑战与解决方案

随着跨境电商的蓬勃发展,品牌保护和知识产权问题日益成为业界关注的焦点。在全球范围内进行电商业务,涉及到多国法律法规的复杂性,品牌所有者需要面对一系列法律挑战,保护其品牌和知识产权不受侵害。本文将深入探讨跨境电商中面临…

L1-016:查验身份证

题目描述 一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下: 首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2&#xf…

P1 Orange Pi Zero2镜像烧录和环境搭建(换源)

目录 前言 01 镜像下载 02 镜像烧录 03 SD卡启动 04 SSH登录开发板 05 基于官方外设开发 方法1 方法2 前言 此专栏为智能家居项目,使用的开发板一共有三块,分别为 主控:Orange Pi Zero2触摸屏控制面板:i.MX 6ULL子系统&…

字符指针变量数组指针变量

字符指针变量 在指针的类型中&#xff0c;我们知道有一种指针叫做字符指针 一般的使用情况如下&#xff1a; #include<stdio.h> int main() {char ch w;char* pa &ch;*pa h;printf("%c", *pa);return 0; } 还有一种使用方法如下&#xff1a; #incl…

国内 AI 成图第一案!你来你会怎么判?

我国目前并未出台专门针对网络爬虫技术的法律规范&#xff0c;但在司法实践中&#xff0c;相关判决已屡见不鲜&#xff0c;K 哥特设了“K哥爬虫普法”专栏&#xff0c;本栏目通过对真实案例的分析&#xff0c;旨在提高广大爬虫工程师的法律意识&#xff0c;知晓如何合法合规利用…

室内外融合便携式定位终端5G+UWB+RTK

一、介绍 便携式定位终端主要用于提供高精度的位置数据&#xff0c;支持室内UWB定位和室外北斗系统定位功能&#xff0c;支持5G公网和5G专网通信功能&#xff0c;便携式定位终端中超宽带(UWB)和实时动态(RTK)技术的集成代表了精确位置跟踪方面的重大进步。这款UWBRTK便携式定位…

git 本地改动无法删除

1. 问题 记录下git遇到奇怪的问题&#xff0c;本地有些改动不知道什么原因无法删除 git stash&#xff0c; git reset --hard HEAD 等都无法生效&#xff0c;最终通过强制拉取线上解决 如下图&#xff1a; 2. 解决 git fetch --all git reset --hard origin/master执行这两…

Android RatingBar实现五星好评

属性 isIndicatorRatingBar 是否为指示器&#xff0c;为true时&#xff0c;用户将无法交互操作&#xff0c;默认为false。 numStars 显示的星型数量&#xff0c;必须是一个整形值&#xff0c;像“50”&#xff0c;虽然可以设置很大&#xff0c;但一般…

STM32-新建工程(标准库)

目录 STM32F10x新建工程&#xff08;标准库&#xff09; 移植文件夹 新建工程 添加启动文件和必需文件 在工程中加载新添加的文件 在工程中添加文件路径 在工程中添加main函数 添加lib库 添加必需文件 添加宏定义 STM32F10x新建工程&#xff08;标准库&#xff09; …