代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上也不关心是否准确得到该对象,它只要一个能够提供该功能的对象即可,此时就可以返回该对象的代理。
在这个设计模式中,需要一个对象所需要实现的接口,然后需要一个真实对象类以及一个代理对象类,这两个类都需要实现此接口。
举一个例子,在多线程的处理上使用的就是代理设计模式。Runnable作为对象接口,MyThread类作为我们自己定义的真实业务类,而Thread类作为代理类来确认客户端是否需要调用run方法。
以下用代理模式来演示一个是否绘制图片的具体例子。
首先,定义提供一个Image接口。
interface Image {
void show();
}
再提供一个真实实现类。
class RealImage implements Image {
public RealImage() {
try {
//程序暂停2s模拟系统开销
Thread.sleep(2000);
System.out.println("图片正在装载中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void show() {
System.out.println("成功绘制实际的图片");
}
}
最后提供一个代理实现类。
class ProxyImage implements Image {
private Image image;
public ProxyImage(Image image) {
this.image = image;
}
@Override
public void show() {
//只有当真正需要调用image的show方法时才创建被代理对象
if (image == null) {
image = new RealImage();
image.show();
} else {
System.out.println("此业务无序绘制图片");
}
}
}
提供一个测试用例。
public static void main(String[] args) {
long start = System.currentTimeMillis();
//程序返回一个image对象,该对象只是RealImage的代理对象
Image image = new ProxyImage(null);
long mid = System.currentTimeMillis();
System.out.println("系统得到image对象的时间开销:" + (mid - start));
image.show();
long end = System.currentTimeMillis();
System.out.println("系统完成业务的时间开销:" + (end - mid));
}
结果如下:
使用此代理模式有两个好处:
1.把创建RealImage推迟到真正需要它时才创建,这样能保证前面程序运行的流畅性,而且能减少RealImage在内存中的存活时间,从宏观上节省了系统的内存开销。
2.在有些情况下,也许程序永远都不会真正调用ProxyImage对象的show方法,这意味着系统根本无需创建RealImage对象。在这种情况下,使用代理模式可以显著地提高系统运行性能。
代理模式还有另一种常用场景:当目标对象的功能不足以满足客户端需求时,系统可以为该对象创建一个代理对象,而代理对象可以增强原目标对象的功能,这就是动态代理。
下面举一个用动态代理来实现吃饭的例子。
提供一个指定接口。
interface Lunch {
void eat(String food, Integer num);
}
提供一个真实实现类。
class RealLunch implements Lunch {
@Override
public void eat(String food, Integer num) {
System.out.println("我要吃" + num + "分量的" + food);
}
}
提供一个拦截器。
拦截器就是就是用于增强目标对象的功能。
class TxUtil {
public void preTx() {
System.out.println("======午饭开始前======");
}
public void afterTx() {
System.out.println("======午饭吃完后======");
}
}
提供一个InvocationHandler实现类,该类的invoke方法将会作为代理对象的方法实现。
class MyInvokationHandle implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
TxUtil tx = new TxUtil();
tx.preTx();
Object result = method.invoke(target, args);//通过反射调用真实类的实现方法
tx.afterTx();
return result;
}
}
上面的invoke方法将会作为动态代理对象的所有方法的实现体。
通过这种方式,使得代理对象的方法既回调了被代理对象的方法,并为被代理对象的方法增加了事物功能。
提供一个动态代理工厂。
class MyProxyFactory {
public static Object getProxy(Object target) {
MyInvokationHandle handle = new MyInvokationHandle();
handle.setTarget(target);
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handle);
}
}
上面的动态代理工厂提供了一个getProxy方法,该方法为target对象生产一个动态代理对象。当程序调用动态代理对象的指定方法时,实际上将变为执行MyInvokationHandle对象的invoke方法。
提供一个测试用例。
public static void main(String[] args) {
Lunch target = new RealLunch();
Lunch lunch = (Lunch) MyProxyFactory.getProxy(target);
lunch.eat("宫保鸡丁", 10);
}
结果如下:
从运行的结果可知,动态代理模式可以非常灵活地实现解耦。
当系统需要扩展RealLunch实例的功能时,程序只需要提供额外的拦截器类,并在MyInvokationHandle的invoke方法回调这些拦截器方法即可。