代理模式对其他对象提供一种代理以控制对这个对象的访问。
在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。
笔者总结代理模式的两个核心内容:一是隔离访问者对被访问对象之间的直接交互,对被访问对象的一切操作通过代理对象执行,这一点与装饰模式和外观模式类似;另一方面代理对象对被代理对象的业务逻辑作出修改,可以增加、屏蔽部分业务逻辑,这一点是装饰和外观模式所不允许的。
代理模式应用最多的场景是对业务访问进行前置和后置处理。 举一个唱歌的例子,原来有一个歌手唱歌的对象,现在歌手要登台演出,唱歌前后要加上报幕和谢幕的动作。歌手不是每次都是登台演出,所以直接修改唱歌的类加上报幕和谢幕的操作显然不合适,这里使用代理模式来解决这个问题:
[java]
interface Sing {
void sing();
}
class Fancy implements Sing {
@Override
void sing() {
System.out.println("唱歌!");
}
}
public class FancyProxy implements Sing {
Sing singger;
public FancyProxy(Sing singger) {
this.singger = singger;
}
@Override
public void sing() {
//报幕
baoMu();
//唱歌
singger.sing();
//谢幕
xieMu();
}
}
上面的方式很好的解决了登台演出的问题,但这样解决方法必须在设计阶段完成,具有很大的局限性。JDK提供了动态创建代理的工具,使得我们在运行时可以生成对象的代理,与上面的代码不同,这里就是我们常说的动态代理
[java]
class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before calling " + method);
method.invoke(target, args);
System.out.println("after calling " + method);
return null;
}
static void main(String[] args) {
Fancy fancy = new Fancy(); // 在这里指定被代理类
InvocationHandler handler = new DynamicProxy(fancy); // 初始化代理类
Class cls = fancy.getClass();
Sing sing = (Sing) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), handler);
sing.sing();
}
}
通过这种方式,被代理的对象(Fancy)可以在运行时动态改变,需要控制的接口(Sing接口)可以在运行时改变,控制的方式(DynamicProxy类)也可以动态改变,从而实现了非常灵活的动态代理关系。