定义
代理模式(Proxy Pattern):给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
通俗理解
在好几年前,买火车票和飞机票总要在线下买。可以在火车站、机场买,当然并不是每一个人都在火车站、机场旁边,如果走很远的路就为了一张票,就太浪费时间了,所以随地可见的火车、飞机票代售点就成为大家买票的最佳选择。
火车、飞机票代售点是火车站、机场的一个代理商,它可以完成火车票、飞机票的销售,同时,还可以在这上面添加自己的功能,例如可以卖一些保险、旅途中的附加服务等等。当然,这其中也有代售点不可以做的工作,其中重要的一点就是代售点不可以进行退票的工作,要退票,只能去火车站、机场。
代理模式就是这样一个场景。如果一个接口很难进行访问,那么我们可以在这个接口上面封装一层代理类,用代理类去调取这个接口,然后我们的系统调取代理类。并且在代理类上面可以封装自己的方法,使得接口更符合我们系统,更加易用。
示例
以火车代售点作为实例。
渣渣程序
车站接口以及实现
public interface IStation {
void saleTicket();
}
public class StationImpl implements IStation {
public void saleTicket() {
System.out.println("火车站卖出一张车票");
}
}
程序主入口
public class Main {
public static void main(String[] args) {
IStation station = new StationImpl();
station.saleTicket();//火车站卖出一张车票
}
}
上面就是完全没有使用代理模式的代码,直接调用车站的代码,就相当于直接去车站买车票一样。当然不是不可以,但是像前面讲的一样,有时候我们去不了车站或者不想去车站的情况下,那么这种写法就不适合了。
优化
普通代理
车站接口以及实现不变,添加StationProxy
的类,实现对Station的代理,程序如下:
public class StationProxy implements IStation {
private IStation station;
public void saleTicket() {
if(station == null) {
station = new StationImpl();
}
System.out.println("连接火车站系统连接");
station.saleTicket();
System.out.println("断开火车站系统连接");
}
}
程序入口
public class Main {
public static void main(String[] args) {
IStation station = new StationProxy();
station.saleTicket();
//连接火车站系统连接
//火车站卖出一张车票
//断开火车站系统连接
}
}
这样就实现了对火车站的代理,并且在代理的方法上面添加了自己系统的方法。要注意的是,这里使用了懒加载的方式,只有在调用代理方法的时候才会初始化对象,这个过程叫懒加载
,有利于改善系统性能。
动态代理
Java提供了反射的方式,可以动态加载类,这样绕过了编译,也让系统实现更加灵活。同时,在代理模式当中,也可以使用动态代理的方式。
车站接口以及实现不变,创建动态代理的处理类。
动态代理处理类
public class StationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = new StationImpl();
System.out.println("代理类:" + proxy.getClass().getName()
+ "调用:"+obj.getClass().getName()
+ "代理方法:"+method.getName());
Object invoke = method.invoke(obj, args);
System.out.println("调用结束");
return invoke;
}
}
程序主入口
public class Main {
public static void main(String[] args) {
IStation stationProxy = (IStation) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{IStation.class},
new StationHandler()
);
stationProxy.saleTicket();
//代理类:com.sun.proxy.$Proxy0调用:com.wusicheng.e24_proxy_pattern.nevv.dynamic.StationImpl代理方法:saleTicket
//火车站卖出一张车票
//调用结束
}
}
动态代理是框架的基础,AOP就是这么实现的。如果跟踪源代码,可以看到大量的invoke,$proxy(),他们用的就是动态代理。这里不做深入,以后读源代码的时候再深入,现在知道,会写就可以了。
优点
- 协调调用者和被调用者,降低系统的耦合度;
- 客户端可以对调用发进行编程,增加或者修改代理类的时候,不需要修改源代码就可以实现,符合“开闭原则”。
缺点
- 封装了一层,系统可能会变慢;
- 实现代理需要额外的工作,有些的实现过程比较复杂。
应用场景
- 对象载入时间长的,需要知道中间状态的;
- 在远程计算机上,访问困难时间长,需要鉴权的;
- 频繁查询和引用的;
- 多线程中使用保证安全的;
- 某对象被多个对象引用,其中的引用需要被改写的时候复制出来的;
- 对象的使用时需要自动做其他工作的。
吐槽
就是在原来的对象上面封装一层。