当前位置: 首页 > news >正文

学习设计模式之代理模式,但是宝可梦

前言

作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。

代码同步更新到 github ,要是点个Star您就是我的神

目录

  • 前言
  • 代理模式
    • 1.情景模拟
      • 1.1静态代理
        • 优点
        • 局限
      • 1.2 动态代理
    • 2.应用
    • 3.局限
    • 4.解决方案CGLIB
      • 踩坑注意!!

代理模式

代理模式是一种结构型设计模式。
对于代理模式,其实不难理解,就是甲乙双方在做一件事的时候,有一个中间人作为代理。
甲委托代理,代理和乙对接。生活中的例子就是租房、房东、中介的关系,租房和房东作为甲乙双方,通过中介完成业务。

在代码开发中,代理模式主要用于控制对对象的访问,通过中介,避免调用者和提供方法的对象直接接触。

1.情景模拟

代理模式的主要抽象思路就是:A和B的直接交互变为A和B通过C来交互。
在宝可梦没血的时候,我们会选择对其进行治疗,我们可以通过背包里的伤药(直接接触),或者宝可梦中心(通过代理)。
于是我们首先抽象出代理模式的第一个概念: Subject抽象主题——回血,以及Real Subject真实主题——实现类

/*** 休息的地方* 提供回血方法*/
public interface Rest {void heal();
}/*** 回血的具体实现类*/
public class RestImpl implements Rest{@Overridepublic void heal() {System.out.println("治疗...");}
}

宝可梦中心作为代理类,自然要先懂得业务,所以代理类也要实现对应的接口:

/*** 静态代理类*/
public class PokemonCenterProxy implements Rest{// 被代理的角色private Rest rest;public PokemonCenterProxy(Rest rest) {this.rest = rest;}@Overridepublic void heal() {System.out.println("在宝可梦中心...");rest.heal();}
}

1.1静态代理

这样,当主角(即程序调用者)想要回血的时候,我们可以直接找到宝可梦中心(代理类):

public class StaticProxyDemo {public static void main(String[] args) {// 真实主题RestImpl rest = new RestImpl();// 传入代理类PokemonCenterProxy pokemonCenterProxy = new PokemonCenterProxy(rest);// 代理类来执行方法pokemonCenterProxy.heal();}
}
在宝可梦中心...
治疗...

优点

可以发现,我们仍然调用了原有对象的heal()方法,但是我们在此基础上,完成了方法的扩展。
即我们在没有修改原有实现类的基础上,实现了新增执行前后的动作的功能,我们甚至可以:

    @Overridepublic void heal() {System.out.println("在宝可梦中心...");rest.heal();System.out.println("按摩SPA");}

可能大家看到这里就比较眼熟,这不可以实现日志功能吗?没错,我们可以在方法执行前后织入另外的行为。
这样做的局限也很明显。

局限

从代理类的代码可以看出,我提供了一个构造方法,传入Rest接口的实现类,这样避免了有新的实现类的时候要再写对应的新的静态代理类的情况。
这样做的问题在于,我们仍然把被代理类给暴露出来了,仍然要先new一个Rest的实现类。
其次,如果Rest接口的方法增多,作为继承了接口的静态代理类,仍要实现每个方法,可能之间有大量的冗余代码。

所以要解决以上局限,动态代理是个更好的选择。

1.2 动态代理

Java对动态代理提供了支持。
要实现动态代理,第一步是实现内置的InvocationHandler接口,重写Invoke方法

public class DynamicProxy implements InvocationHandler {private Rest rest;public DynamicProxy(Rest rest) {this.rest = rest;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("进入宝可梦中心");Object invoke = method.invoke(rest, args);System.out.println("售后服务");return invoke;}
}

基于反射,程序在运行途中获得了类的方法(invoke的参数中的method),方法的参数等信息。
被代理的类,运行的所有方法都被替换为这个invoke方法,真正执行原方法的逻辑是在method.invoke(rest, args);
所以这一行的前后,就可以写代理类在执行方法之前/之后的逻辑。

public class DynamicProxyDemo {public static void main(String[] args) {// 需要被代理的对象Rest rest = new RestImpl();// 以此创建代理类DynamicProxy dynamicProxy = new DynamicProxy(rest);ClassLoader classLoader = rest.getClass().getClassLoader();Rest o = (Rest) Proxy.newProxyInstance(classLoader, new Class[]{Rest.class}, dynamicProxy);o.heal();}
}

动态代理的核心就在于Proxy类,在动态代理中,创建真实对象的实例是通过Proxy的newProxyInstance方法。
其参数有三:

  • 类加载器:接口类的类加载器,直接调用api获取
  • 实现的接口的数组: new Class[]{...}是创建数组并赋值的语法,里面传入要实现的接口的类对象
  • 实现了InvocationHandler的对象,用来执行逻辑

然后用Proxy类创建出的对象调用方法,就可以实现代理类中实现的逻辑,无论Rest接口有多少方法,我们都不需要一一去实现。
相应地,有新业务接口的时候,也不用新增代理类。除非你有不同的代理逻辑(即invoke方法里的逻辑),否则都不需要新增代码。
运行结果:

进入宝可梦中心
治疗...
售后服务

2.应用

Spring框架中AOP就是基于动态代理,以此来实现一种切面逻辑。
应用场景包括:日志记录、权限控制。

3.局限

通过Proxy类来实现动态代理有一个最主要的局限:只能代理接口类。
并且通过反射来实现的性能开销比较大。

4.解决方案CGLIB

通过CGLIB实现动态代理。CGLIB可以实现对类的动态代理,并且实现原理是生成新的字节码类。
第一步:引入依赖

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

第二部:写要代理的类(这里可以不是接口了)

public class Heal {public void heal(){System.out.println("HP+++");}
}

第三步:创建代理类

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {// 代理逻辑System.out.println("Pre");Object o1 = methodProxy.invokeSuper(o, objects);System.out.println("Suf");return o1;}
}

第四步:创建代理对象

public class Demo {public static void main(String[] args) {// 创建Enhancer类,类似于Proxy类Enhancer enhancer = new Enhancer();// 设置目标类enhancer.setSuperclass(Heal.class);// 设置拦截器enhancer.setCallback(new MyMethodInterceptor());// 创建代理对象Heal proxy = (Heal)enhancer.create();// 执行原有方法proxy.heal();}
}

踩坑注意!!

如果跟我一样用的Java17, 那么运行的时候会出现:

Exception in thread "main" java.lang.ExceptionInInitializerErrorat com.example.springbootdemo.proxyCglib.Demo.main(Demo.java:7)
Caused by: net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @14899482at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:464)at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)at net.sf.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221)at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:174)at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:153)at net.sf.cglib.proxy.Enhancer.<clinit>(Enhancer.java:73)... 1 more

解决方法和原因: https://blog.csdn.net/guoshengkai373/article/details/127319933

只能换到低版本的JDK,我试过把CGLIB依赖版本弄到最新,也没用。

相关文章:

学习设计模式之代理模式,但是宝可梦

前言 作者在准备秋招中&#xff0c;学习设计模式&#xff0c;做点小笔记&#xff0c;用宝可梦为场景举例&#xff0c;有错误欢迎指出。 代码同步更新到 github ,要是点个Star您就是我的神 目录 前言代理模式1.情景模拟1.1静态代理优点局限 1.2 动态代理 2.应用3.局限4.解决方…...

自学Python01-创建文件写入内容

此处省去安装和前言&#xff0c;需要两个东西 一个去下载安装python官方库 Welcome to Python.org 一个是编译器pycharm PyCharm 安装教程&#xff08;Windows&#xff09; | 菜鸟教程 PyCharm: the Python IDE for Professional Developers by JetBrains 第一节 练习print…...

Qt —UDP通信QUdpSocket 简介 +案例

1. UDP通信概述 UDP是无连接、不可靠、面向数据报&#xff08;datagram&#xff09;的协议&#xff0c;可以应用于对可靠性要求不高的场合。与TCP通信不同&#xff0c;UDP通信无需预先建立持久的socket连接&#xff0c;UDP每次发送数据报都需要指定目标地址和端口。 QUdpSocket…...

五大类注解和方法注解详解

五大类注解为Controller&#xff0c;Service&#xff0c;Repository&#xff0c;Configuration&#xff0c;Component,方法注解为Bean。 需要注意的是&#xff1a;Bean注解必须要在类注解修饰的类内才能正常使用。 一、与配置文件的关系 在spring原生项目中 如果你使用的spri…...

机器人中的数值优化(十)——线性共轭梯度法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…...

数据结构与算法之贪心动态规划

一&#xff1a;思考 1.某天早上公司领导找你解决一个问题&#xff0c;明天公司有N个同等级的会议需要使用同一个会议室&#xff0c;现在给你这个N个会议的开始和结束 时间&#xff0c;你怎么样安排才能使会议室最大利用&#xff1f;即安排最多场次的会议&#xff1f;电影的话 那…...

【网络编程】网络基础概念

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;数据结构&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;网络编程等领域UP&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff0…...

连接虚拟机报错 Could not connect to ‘192.168.xxx.xxx‘ (port 22): Connection failed.

使用xshell连接虚拟机报错 Connecting to 192.168.204.129:22… Could not connect to ‘192.168.204.129’ (port 22): Connection failed. Type help’ to learn how to use Xshell prompt. 按网上的方法 是否能ping通内外网 ping www.baidu.com防火墙是否关闭 firewal…...

数学建模--Topsis评价方法的Python实现

目录 1.算法流程简介 2.算法核心代码 3.算法效果展示 1.算法流程简介 """ TOPSIS(综合评价方法):主要是根据根据各测评对象与理想目标的接近程度进行排序. 然后在现有研究对象中进行相对优劣评价。 其基本原理就是求解计算各评价对象与最优解和最劣解的距离…...

超越时间与人力的软件开发智慧:《人月神话》

目录 1、写在前面2、沟通&#xff01;沟通&#xff01;沟通&#xff01;3、“银弹论”4、“人月神话”不能成立的原因5、影响力6、图书推荐 1、写在前面 《人月神话》是由计算机科学家弗雷德里克布鲁克斯所著的一本经典著作&#xff0c;首次出版于1975年。这本书以一个个小故事…...

Java Stream 流对象(实用技巧)

目录 一、InputStream & OutputStream 1.1、InputStream 和 OutputStream 一般使用 1.2、特殊使用 1.2.1、如何表示文件读取完毕&#xff1f;&#xff08;DataInputStream&#xff09; 1.2.2、字符读取/文本数据读取&#xff08;Scanner&#xff09; 1.2.3、文件的随机…...

【用unity实现100个游戏之8】用Unity制作一个炸弹人游戏

文章目录 前言素材开始一、绘制地图二、玩家设置三、玩家移动四、玩家四方向动画运动切换 五、放置炸弹六、生成爆炸效果七、墙壁和可破坏障碍物的判断八、道具生成和效果九、玩家死亡十、简单的敌人AI十一、简单敌人AI十二、随机绘制地图十三、虚拟摇杆 最终效果待续源码完结 …...

简易版人脸识别qt opencv

1、配置文件.pro #------------------------------------------------- # # Project created by QtCreator 2023-09-05T19:00:36 # #-------------------------------------------------QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsTARGET 01_face TEMP…...

如何系统地学习 JavaScript?

前言 在学习JavaScript前需要先将Html和Css的相关知识点弄清楚&#xff0c;Js的很多操作是要结合Html和Css&#xff0c;下面我总结了Html、Css和Js的相关学习知识点供参考&#xff0c;希望对你有所帮助喔~ Html 文档学习 【HTML 】w3school教程 :https://www.w3school.com.…...

对称二叉树(Leetcode 101)

题目 101. 对称二叉树 思路 使用层序遍历&#xff0c;遍历当前层的节点时&#xff0c;如该节点的左&#xff08;右&#xff09;孩子为空&#xff0c;在list中添加null&#xff0c;否则加入左&#xff08;右&#xff09;孩子的值。每遍历完一层则对当前list进行判断&#xff0c…...

动手学深度学习(2)-3.5 图像分类数据集

文章目录 引言正文图像分类数据集主要包介绍主要流程具体代码练习 总结 引言 这里主要是看一下如何加载数据集&#xff0c;并且生成批次训练的数据。最大的收获是&#xff0c;知道了如何在训练阶段提高模型训练的性能 增加batch_size增加num_worker数据预加载 正文 图像分类…...

C标准输入与标准输出——stdin,stdout

&#x1f517; 《C语言趣味教程》&#x1f448; 猛戳订阅&#xff01;&#xff01;&#xff01; ​—— 热门专栏《维生素C语言》的重制版 —— &#x1f4ad; 写在前面&#xff1a;这是一套 C 语言趣味教学专栏&#xff0c;目前正在火热连载中&#xff0c;欢迎猛戳订阅&#…...

如何将home目录空间扩充到根目录下

目录 1、查看查看磁盘使用情况2、扩容思路3、卸载并删除/home4、扩大/root逻辑卷5、扩大/文件系统6、重建/home逻辑卷7、创建/home文件系统8、将新建的文件系统挂载到/home目录下9、恢复/home并删除备份10、再次查看看磁盘存储 系统&#xff1a;centos7.9 1、查看查看磁盘使用…...

Ceph PG Peering数据修复

ceph数据修复 当PG完成了Peering过程后&#xff0c;处于Active状态的PG就可以对外提供服务了。如果该PG的各个副本上有不一致的对象&#xff0c;就需要进行修复。 Ceph的修复过程有两种&#xff1a;Recovery和Backfill。 Recovery是仅依据PG日志中的缺失记录来修复不一致的对…...

服务器上使用screen和linux的基本操作

临时换源 pip install torch1.7.1 -i https://pypi.tuna.tsinghua.edu.cn/simple some-package pip install torch1.7.1 -i http://pypi.douban.com/simple some-package临时清华源和豆瓣源 配环境的一点小问题 我们尽量是去配置能满足代码的环境&#xff0c;而不要想着修改…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

多元隐函数 偏导公式

我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式&#xff0c;给定一个隐函数关系&#xff1a; F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 &#x1f9e0; 目标&#xff1a; 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z​、 …...

MeshGPT 笔记

[2311.15475] MeshGPT: Generating Triangle Meshes with Decoder-Only Transformers https://library.scholarcy.com/try 真正意义上的AI生成三维模型MESHGPT来袭&#xff01;_哔哩哔哩_bilibili GitHub - lucidrains/meshgpt-pytorch: Implementation of MeshGPT, SOTA Me…...

深入理解 React 样式方案

React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...