在某些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。
现实生活中场景:
-
购买火车票不是去火车站买,而是通过12306进行购买。
-
去租房,不是直接找房东,而是通过中介租房。
程序场景:
-
要访问的远程对象比较大(如视频或大图像等)。
-
因为安全问题原因需要屏蔽客户端直接访问真实对象,如国家单位的内部数据库。
定义:
由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不合适或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
优点:
-
在客户端与目标对象之间起到一个中介作用和保护目标对象的作用。
-
代理对象可以扩展目标对象的功能。
-
将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性。
缺点:
-
会造成系统设计中类的数量增加。
-
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢。
-
增加了系统的复杂度。
模式结构:
主要角色:
1.抽象主题类:通过接口或者抽象类声明真实主题和代理对象的业务方法。
2.真实主题类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
3.代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。
代理的分类:
根据代理的创建时期,代理模式分为静态代理和动态代理。
-
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了。
-
动态:在程序运行时,运用反射机制动态创建而成。
静态代理:
示例:
public class ProxyTest { public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.Request(); } }
//抽象主题 interface Subject { void Request(); }
//真实主题 class RealSubject implements Subject { public void Request() { System.out.println("访问真实主题方法..."); } }
//代理 class Proxy implements Subject { private RealSubject realSubject; public void Request() { if (realSubject == null) { realSubject = new RealSubject(); } preRequest(); realSubject.Request(); postRequest(); } public void preRequest() { System.out.println("访问真实主题之前的预处理。"); } public void postRequest() { System.out.println("访问真实主题之后的后续处理。"); } }
运行结果:
访问真实主题之前的预处理。 访问真实主题方法... 访问真实主题之后的后续处理。
应用场景:
-
远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
-
虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
-
安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
-
智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
-
延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
动态代理-代理模式的扩展
在静态代理模式中,代理类中包含了对真实主题的引用,这种方式存在两个缺点。
-
真实主题与代理主题必须一一对应,增加真实主题也要增加代理。
-
设计代理之前真实主题必须事先存在,不太灵活。
示例:
public interface IPerson { void findTeacher(); //找老师 }
public class JdkFuDao implements InvocationHandler { private IPerson target; public IPerson getInstance(IPerson target) { this.target = target; Class<?> clazz = target.getClass(); return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(this.target, args); after(); return result; } private void after() { System.out.println("双方同意,开始辅导"); } private void before() { System.out.println("已经收集到您的需求,开始挑选老师"); } }
public class ZhaoLiu implements IPerson { @Override public void findTeacher() { System.out.println("符合赵六的要求"); } public void buyInsure() { } }
public class Test { public static void main(String[] args) { JdkFuDao jdkFuDao = new JdkFuDao(); IPerson zhaoliu = jdkFuDao.getInstance(new ZhaoLiu()); zhaoliu.findTeacher(); } }
核心原理:向代理中传入需要反射创建的对象。
静态代理与动态代理的区别:
-
静态代理只能通过手动完成代理的操作,如果被代理类增加了新方法,则代理类需要同步增加,违背开闭原则。
-
动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
-
若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
标签:真实,对象,void,主题,代理,模式,public 来源: https://www.cnblogs.com/l12138h/p/16306336.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。