代理模式
代理模式
一、什么代理模式?
代理模式(Proxy Design Pattern):在不改变原始类(被代理类)的情况下,对它进行功能的增强。
代理模式有几个特点:
- 不会改变原始类(被代理类)
- 核心功能还是由原始类(被代理类)实现
- 代理类只负责增强的那部分功能
代理,就是帮别人做事情,核心工作还是由被代理人做,但是其他琐碎的事则可以交给代理人。
比如租房时,我们可以找中介帮忙找房子、等一系列事,减少自己的工作量。
这里的中介就是一个代理,我们自己就是被代理人。
二、为什么要用代理模式?
使用代理模式主要有几个目的:
- 保护目标对象
- 增强目标对象
- 隔离功能
保护对象,就是利用代理对象,隔离原始对象的数据,以达到保护对象的目的。
比如说,通过代理类去验证权限,有权限时才可以访问原始对象的数据。
增强对象,就是原始对象功能比较简单,可以通过代理类为其增加额外功能。
比如说,通过代理类去增加缓存,来提高接口的访问速度。
隔离功能,比如隔离业务功能和非业务功能,把非业务功能的代码放在代理类实现。
比如,接口监控、统计、鉴权、限流、事务、幂等、日志等。
三、代理模式怎么用?
3.1 静态代理
静态代理,就是每个被代理对象,都需要编写一个代理类。
这就是说,编写原始类时,必须同时编写一个代理类,否则就没有办法实现代理模式了。
比如说,有100个原始类:Base1
、Base2
…… Base100
。
那么,静态代理实现,就需要同时编写 100 个代理类:Proxy1
、Proxy2
…… Proxy100
。
(真要这么写,手都得废了~~~~~)
3.2 动态代理
动态代理,就是解决静态代理的问题,改成由程序动态生成代理类,无需自己编写代理类。
比如说,静态代理需要编写100个代理类:Proxy1
、Proxy2
…… Proxy100
。
如果是动态代理,只需1个代理生成类即可:ProxyCreator
,用来生成不同的代理类。
动态代理,实际就是搞了一个工厂类,专门用于生成代理类,减少手动编写的工作量。
动态代理,虽然不需要自己写代理类,但是一般都需要编写生成动态代理类的代码。
不过像生成动态代理类这种代码,实际上有很多框架都实现了,比如Spring、CgLib等,直接用即可。
3.3 模型结构
代理模型中的角色包括:
- 抽象主题(SUbject):被代理的目标类接口,或者抽象类
- 真实主题(RealSubject):被代理目标的实现类
- 代理角色:代理的实现类,实现了抽象主题接口,内部蕴含真实主题的引用
模型结构:
示例程序结构:
抽象主题(Subject):
1 | public interface Printable { |
真实主题(RealSubject):
1 | public class Printer implements Printable { |
代理角色(Proxy):
1 | public class ProxyPrinter implements Printable { |
四、代理模式有什么优缺点?
优点:
- 代理对象对原始对象起到了保护作用
- 代理对象可以对原始对象进行扩展
缺点:
- 每个原始对象都需要有自己的代理类
- 代理类相当于多了一层中介,而且还增加了功能,接口的访问速度变慢了
- 系统的复杂度增加了,代码调试变得更麻烦
五、代理模式的形式
- 远程代理:隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,JAVA 中的 RMI(RemoteMethodInvocation:远程方法调用),调用远程方法接口,就像是在本地调用一样。
- 虚拟代理:当创建的目标对象开销很大时,只有当真正需要实例时,才会生成和初始化实例。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
- 安全代理:控制不同种类客户对真实对象的访问权限,比如不同客户允许调用的接口不一样。
- 智能指引:附加一些额外的处理功能。例如,计算接口的访问次数,可以在代理类中处理。
- 延迟加载:指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。
六、和其他模式的对比
6.1 代理 vs. 装饰器
装饰器模式和代理模式的模式结构上很类似,但是它们的使用目的不同:
- 装饰器模式:偏向于给原始对象增加新的业务功能
- 代理模式:偏向于给原始对象减轻负担,或者说增加非业务功能