系列文章
【设计模式】七大设计原则
【设计模式】第一章:单例模式
【设计模式】第二章:工厂模式
【设计模式】第三章:建造者模式
【设计模式】第四章:原型模式
【设计模式】第五章:适配器模式
【设计模式】第六章:装饰器模式
【设计模式】第七章:代理模式
【设计模式】第八章:桥接模式
【设计模式】第九章:外观模式 / 门面模式
【设计模式】第十章:组合模式
【设计模式】第十一章:享元模式
【设计模式】第十二章:观察者模式
【设计模式】第十三章:模板方法模式
【设计模式】第十四章:策略模式
【设计模式】第十五章:责任链模式
【设计模式】第十六章:迭代器模式
【设计模式】第十七章:状态模式
【设计模式】第十八章:备忘录模式
【设计模式】第十九章:访问者模式
【设计模式】第二十章:解释器模式
【设计模式】第二十一章:命令模式
【设计模式】第二十二章:中介者模式
文章目录
- 系列文章
- 一、定义
- 二、角色分类
- 三、UML图
- 四、实现方式
- 需求分析
- 具体实现
- **简单实现**
- **动态代理**
- 五、应用场景
- 六、优缺点
- **优点**
- 缺点
一、定义
摘自百度百科: 所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。
二、角色分类
抽象主题角色(Subject)
定义了代理角色和真实主题角色的共同接口,代理角色通过该接口调用真实主题角色的方法。
真实主题角色(Real Subject)
实现了抽象主题角色的接口,代表真实的业务对象。
代理角色(Proxy)
实现了抽象主题角色定义的接口,并持有真实主题角色的引用,代表了真实主题角色的代理。在代理角色中,可以添加额外的功能,如记录日志、权限控制等,而这些功能并不是真实主题角色本身所具备的功能
客户(Client)
具体调用代理者的角色
三、UML图
四、实现方式
需求分析
假如我们有一个这样的场景:我们想要去看电影,但是我们嫌麻烦不想去电影院排队买票,这时候我们可以选择第三方的平台来买票,并且代理会为我们处理一切的购票相关的事务,包括选座、付款等。其实这种模式就相当于我们设计模式中的代理模式,其代理第三方平台,接下来我们用代码来实现一下这个业务。
具体实现
简单实现
抽象主题角色(Subject)
public interface Subject {
void request();
}
真实主题角色(Real Subject)
public class RealSubject implements Subject {
public void request() {
System.out.println("RealSubject: 处理请求中");
}
}
代理角色(Proxy)
public class Proxy implements Subject {
private RealSubject realSubject;
public void request() {
if (null == realSubject) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("Proxy: 请求前执行逻辑");
}
private void postRequest() {
System.out.println("Proxy: 请求后执行逻辑");
}
}
客户角色(Client)
public class Client {
public static void main(String[] args) {
Subject subject = new Proxy();
subject.request();
}
}
运行结果
Proxy:请求前执行逻辑
RealSubject: 处理请求中
Proxy: 请求后处理逻辑
动态代理
我们有一个接口 Subject 和其一个实现类 RealSubject,我们要使用JDK的动态代理来生成一个代理对象Proxy,代理对象的调用会转发给RealSubject对象。
抽象主题角色(Subject)
public interface Subject {
void request();
}
真实主题角色(Real Subject)
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: 处理请求中");
}
}
动态代理角色(DynamicProxy)
public class DynamicProxy implements InvocationHandler {
private Object realObject;
public DynamicProxy(Object realObject) {
this.realObject = realObject;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用前执行
System.out.println("DynamicProxy: 请求后执行逻辑");
// 执行实际方法
Object result = method.invoke(realObject, args);
// 调用后执行
System.out.println("DynamicProxy: 请求后执行逻辑");
return result;
}
}
客户角色(Client)
public class Client {
public static void main(String[] args) {
// 创建被代理对象
RealSubject realSubject = new RealSubject();
// 创建代理对象
Subject proxy = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader,
realSubject.getClass().getInterfaces(),
new DynamicProxy(realSubject)
);
// 调用代理对象的方法
proxy.request();
}
}
运行结果
DynamicProxy: 请求前执行逻辑
RealSubject: 处理请求中
DynamicProxy: 请求后执行逻辑
在上述代码中,RealSubject
表示真实的业务实现类,DynamicProxy
表示动态代理类,Client
表示客户端。我们通过创建RealSubject
对象,然后创建一个代理对象proxy
来访问RealSubject
。
在代理对象中,我们实现了InvocationHandler
接口,并重写了其中的invoke()
方法,在调用代理对象的方法时,invoke()
方法会被自动调用。在invoke()
方法中,我们可以进行一些额外的操作,比如在调用实际对象的方法前后添加日志等。
使用Proxy.newProxyInstance()
方法来创建代理对象。该方法需要传入三个参数:ClassLoader
对象,Class
对象数组和InvocationHandler
对象。其中,ClassLoader
对象用于加载代理类,Class
对象数组表示代理类需要实现的接口列表,InvocationHandler
对象用于处理代理对象的方法调用。
五、应用场景
以下部分内容摘自菜鸟教程
意图: 为其他对象提供一种代理,以控制对这个对象的访问。
主要解决: 解决了在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用: 想在访问一个类时做一些控制。
如何解决: 增加中间层。
关键代码: 实现与被代理类组合。
应用实例:
- Windows中的快捷方式
- 猪八戒去找高翠兰,结果却是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒在访问高翠兰的时候看不出来这个是悟空,所以此时孙悟空是高翠兰的代理类
- 买火车票不一定在火车站买,也可以去代售点
- 一张支票或银行存单是账户中资金的代理。支票在市场交易中用来替代现金,并提供对签发账号上资金的控制
- Spring AOP
适用场景:
按职责划分通常有以下场景:
- 远程代理
- 虚拟代理
- Copy-on-Write代理
- 保护(Protect or Access)代理
- Cache代理
- 防火墙(Firewall)代理
- 同步化(Synchronization)代理
- 智能引用(Smart Reference)代理
注意事项:
- 和适配器模式区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口
- 与装饰器的区别:装饰器模式为了增强功能,而代理模式是为了加以控制
六、优缺点
优点
- 职责清晰
- 扩展性高
- 智能化
缺点
- 由于在客户端和真实主题中间加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂