代理模式
为另外一个对象提供一个替身或者占位符以控制对这个对象的访问。
当我们不想直接调用这个对象时,使用代理模式来创建代表对象,让这个代表对象控制对某个对象的访问,被代理的对象可以使远程的对象创建开销大的对象或者需要安全控制的对象。比如当我们需要购买一张火车票时,我们可以前往就近的代售点购买而不需要到火车站购买,代售点就是一个代理,这样可以避免不必要的开销。
示意图如下:
一、静态代理
静态代理中有三个角色,分别为:
抽象类:声明目标对象及代理对象,使得任何使用目标对象的地方都可以使用代理对象,也就是“卖票”。
真实类:代理对象所代理的对象,也就是“火车站”“。
代理类:代理对象包含目标对象的引用,这样便能够操作目标对象;而且代理对象提供一个与目标对象相同的接口,以便替代目标对象返回给用户。也就是“代售点”
代码如下:
抽象类:
public interface TicketManager {
//抽象对象有三个功能:售票、改签、退票
public void soldticket();
public void changeticket();
public void returnticket();
}
真实类:
public class TrainStation implements TicketManager{
@Override
public void soldticket() {
// TODO Auto-generated method stub
System.out.println("售票");
}
@Override
public void changeticket() {
// TODO Auto-generated method stub
System.out.println("改签");
}
@Override
public void returnticket() {
// TODO Auto-generated method stub
System.out.println("退票");
}
public void cheak()
{
System.out.println("验票");
}
}
两个代理类(加入身份验证与消息记录功能):
public class StaticProxy implements TicketManager{
TicketManager TM;
public StaticProxy(TicketManager TM)
{
this.TM=TM;
}
@Override
public void soldticket() {
// TODO Auto-generated method stub
check();
TM.soldticket();
}
@Override
public void changeticket() {
// TODO Auto-generated method stub
check();
TM.changeticket();
}
@Override
public void returnticket() {
// TODO Auto-generated method stub
check();
TM.returnticket();
}
public void check()
{
System.out.println("开始验票");
}
}
TicketManager TM;
public LogProxy (TicketManager TM)
{
this.TM=TM;
}
@Override
public void soldticket() {
// TODO Auto-generated method stub
TM.soldticket();
log();
}
@Override
public void changeticket() {
// TODO Auto-generated method stub
TM.changeticket();
log();
}
@Override
public void returnticket() {
// TODO Auto-generated method stub
TM.returnticket();
log();
}
public void log()
{
System.out.println("消息记录");
}
}
用户测试类:
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
TicketManager TM=new LogProxy(new StaticProxy(new TrainStation()));
TM.soldticket();
TM.changeticket();
TM.returnticket();
}
}
输出结果为:
分析这一过程,首先new了一个TrainStation的对象,这个对象中重写了TicketManager的方法,之后通过StaticProxy的构造函数将TrainStation(真实类)对象传入StaticProxy中,这样StaticProxy代理类便获得了被代理类的方法,后续同理。
在这个过程中我们可以发现,当我们的客户通过代理类进行购票时,代理类其实并不能直接将票卖给客户,而是要通过真实对象(火车站)的目标对象来将票卖给客户,也就是代理类起到的承接的作用,相当于一个中介,但是我们可以根据实际情况在中介处添加一些条件及功能,比如身份验证,购买记录等。
优缺点:静态代理对于真实的对象进行了封装,实际调用时不会更改目标类的代码,但是对于代理类而言容易产生代码的冗余,尤其是需要对个类型的代理类时,这个缺点将会放大。
二、动态代理
之前的静态代理中,代理者持有被代理者的引用,二者实现相同的接口。用户只能直接调用代理者的方法,在这个方法中,代理者决定了是否调用被代理者的方法。但是静态代理在代码执行前就已经创建代理对象了,这个代理对象是确定的。而动态代理是在程序执行期间才创建对象,运用到了JAVA的反射机制。
与静态代理不同的是, DynamicProxy类需要实现JDK自带的java.lang.reflect.InvocationHandler接口,这个接口的对象是一个调用处理器,它只有一个方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;/第一个参数,目标对象的装载器;第二个参数,目标接口已实现的所有接口,而这些是动态代理类要实现的接口列表;第三个参数, 调用实现了InvocationHandler的对象生成动态代理实例,当你一调用代理,代理就会调用InvocationHandler的invoke方法。代理对象对任何方法的调用都要通过这个方法,以下给出例子:
真实类的接口和静态代理的相同。
public class DynamicProxy implements InvocationHandler {
private Object targetObject;
/**
* 目标的初始化方法,根据目标生成代理类
*/
public Object newProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
check();
Object ret = null;
try {
// 调用目标方法
ret = method.invoke(targetObject, args);
// 执行成功,打印成功信息
log();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return ret;
}
public void check(){
System.out.println("验票");
}
public void log(){
System.out.println("购票消息" );
}
}
其中采用了反射机制,这样你可以在不知道具体的类的情况下,根据配置的参数去调用一个类的方法。在灵活编程的时候非常有用。
优缺点:
采用动态代理使得代理类更加简单,能够解决需要创建多个代理类的麻烦,避免了多余的代码。但是动态代理的效率相对于静态代理会稍微低一些。
其他代理:
1.避免加载大文件的GUI挂起:比如在当需要从网上加载一张比较大的图片时,主线程是阻塞的,此时就会导致GUI图形界面挂起,无法响应任何操作。此时可以创建一个图片的代理,在加载图片的过程中先在屏幕上显示等待加载的提示符,把加载操作放在线程中执行,加载完之后,代理让出显示的权利,让图片显示在界面上。
2.远程方法调用(RMI):Java编程语言里一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。RMI允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。