目录
代理模式【Proxy Pattern】,什么是代理模式?主要角色?代理模式分类?优缺点?应用场景?代理模式实现?
什么是代理模式?
代理模式(Proxy Pattern)是一种结构型设计模式,它通过为其他对象提供一种代理以控制对这个对象的访问。代理模式主要用于在不修改原对象的前提下,给原对象的功能添加控制、增强或简化客户端的访问。
代理模式的主要角色
(1)抽象主题(Subject)
定义代理和真实对象的共同接口,以便客户端可以通过代理对象或真实对象访问同一组方法。
(2)真实主题(Real Subject)
真正实现业务逻辑的对象。代理对象通过调用真实对象来完成具体的功能。
(3)代理(Proxy)
控制对真实对象的访问,可以在调用真实对象的方法前后添加额外的逻辑,比如权限验证、延迟加载、日志记录等。
代理模式分类
(1)静态代理
代理类和目标类在编译期间就已经确定,代理类通常由程序员手动编写。
(2)动态代理
代理类在运行时动态生成,通过反射机制实现。Java 中的动态代理(如 JDK 动态代理和 CGLIB 动态代理)就是一种常见实现。
代理模式优缺点
优点
(1)控制对真实对象的访问
代理模式能够在不直接访问真实对象的情况下,通过代理对象控制对真实对象的访问。这对于权限控制、资源管理等场景非常有用。
(2)增强对象的功能
代理可以在不修改原有代码的前提下增强真实对象的功能。例如,增加日志记录、监控、缓存、延迟加载等,扩展了对象的行为。
(3)延迟初始化
通过虚拟代理,可以实现真实对象的延迟加载,即当客户端真正需要时才创建该对象,节省了系统资源。
(4)支持远程访问
远程代理模式能够将本地调用代理到远程对象上,实现透明的网络调用,使得分布式系统的实现更加简单。
(5)符合开闭原则
代理模式允许在不修改真实对象代码的情况下添加额外功能,遵循了软件设计的开闭原则(对扩展开放,对修改关闭)。
缺点
(1)增加了系统复杂度
使用代理模式时,系统需要引入一个额外的代理对象,增加了类的数量和系统的复杂性,特别是在动态代理的场景下,调试和排查问题会更麻烦。
(2)性能开销
由于代理模式引入了额外的层次和方法调用,这在某些场景下会导致性能开销。例如,频繁的远程代理调用可能会增加网络延迟,或者动态代理的反射机制会带来一定的性能损耗。
(3)可能滥用代理
代理模式非常灵活,但不适合在所有场景中使用。如果滥用代理模式,会导致系统结构变得复杂,难以维护。代理类的引入应当有明确的目的和需求。
(4)调试难度增大
使用代理模式后,方法调用链条变长,特别是动态代理的使用,可能会使调试、定位问题的难度增加,因为代理类隐藏了对真实对象的直接调用关系。
代理模式应用场景
(1)远程代理
代理远程的对象,通过代理对象来调用远程服务。
(2)虚拟代理
当对象的创建开销较大时,使用代理延迟对象的创建。
(3)保护代理
在调用对象的方法时进行权限控制,确保客户端具有访问权限。
(4)缓存代理
为开销较大的操作提供临时存储,以提高性能。
(5)日志代理
在调用对象的方法时,自动记录日志。
代理模式实现
什么是代理模式呢?
我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代理人虽然不能干活,但是被代理的人能干活呀。
比如:西门庆想找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时这样的:
1、定义一种类型的女人(抽象主题,Subject)
package com.uhhe.common.design.proxy;
/**
* 定义一种类型的女人,王婆和潘金莲都属于这个类型的女人
*
* @author nizhihao
* @version 1.0.0
* @date 2023/2/27 9:38
*/
public interface KindWomen {
/**
* 这种类型的女人能做什么事情呢? 抛媚眼
*/
void makeEyesWithMan();
/**
* happy what? You know that!
*/
void happyWithMan();
}
2、定义潘金莲(真实主题,Real Subject)
package com.uhhe.common.design.proxy;
/**
* 定义一个潘金莲
*
* @author nizhihao
* @version 1.0.0
* @date 2023/2/27 9:39
*/
public class PanJinLian implements KindWomen {
@Override
public void happyWithMan() {
System.out.println("潘金莲在.....");
}
@Override
public void makeEyesWithMan() {
System.out.println("潘金莲抛媚眼");
}
}
3、定义王婆(代理,Proxy)
package com.uhhe.common.design.proxy;
/**
* 王婆这个人老聪明了,她太老了,是个男人都看不上,但是她有智慧有经验呀,她作为一类女人的代理!
*
* @author nizhihao
* @version 1.0.0
* @date 2023/2/27 9:40
*/
public class WangPo implements KindWomen {
private final KindWomen kindWomen;
public WangPo() {
// 默认是潘金莲的代理
this.kindWomen = new PanJinLian();
}
/**
* 她可以是KindWomen的任何一个女人的代理,只要你是这一类型
*
* @param kindWomen 一类女人
*/
public WangPo(KindWomen kindWomen) {
this.kindWomen = kindWomen;
}
@Override
public void happyWithMan() {
// 自己老了,可以让年轻的代替
this.kindWomen.happyWithMan();
}
@Override
public void makeEyesWithMan() {
// 王婆这么大年龄了,谁看她抛媚眼?
this.kindWomen.makeEyesWithMan();
}
}
4、定义西门庆
package com.uhhe.common.design.proxy;
/**
* 定义一个西门庆
*
* @author nizhihao
* @version 1.0.0
* @date 2023/2/27 9:42
*/
public class XiMenQing {
/**
* 代理模式【Proxy Pattern】
* <p>
* 比如:西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理
* <p>
* 水浒里是这样写的:西门庆被潘金莲用竹竿敲了一下难道,痴迷了,被王婆看到了, 就开始撮合两人好事
* 王婆作为潘金莲的代理人收了不少好处费,那我们假设一下:如果没有王婆在中间牵线,这两个的能成吗?难说的很!
* <p>
* 代理模式主要使用了 Java 的多态,干活的是被代理类,代理类主要是接活
* 你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?
* 同根就成,大家知根知底,你能做啥,我能做啥都清楚的很(同一个接口)
*/
public static void main(String[] args) {
// 把王婆叫出来
WangPo wangPo = new WangPo();
// 然后西门庆就说,我要和潘金莲happy,然后王婆就安排了西门庆丢筷子的那出戏
// 看到没,虽然表面上是王婆,实际上是潘金莲
wangPo.makeEyesWithMan();
wangPo.happyWithMan();
}
}