一、定义
代理模式是为其他对象提供一种代理以控制这个对象的访问,一般是不想直接访问某个对象或无法访问到时,可以通过一个代理对象间接访问。
二、实现
代理模式有两种:静态代理和动态代理,我们先来看下静态代理。
这里我们以代驾为例,正常情况下我们可以自己开车,但喝了酒的情况下,就得找一个代驾了,否则将面临罚款扣分和吊销驾照等处罚。
不管是自己开车或叫代驾,两者做的事情本质都相同,那就是开车。所以我们可以先定义委托对象与代理对象需要实现相同的接口。
/**
* 抽象主题类
*/
public interface IDrive {
//开车
void drive();
}
接着我们定义一个具体的实现类ZhangSan
/**
* 具体主题类
*/
public class ZhangSan implements IDrive {
@Override
public void drive() {
System.out.println("开车中...");
}
}
平时都是ZhangSan自己开车上下班,某天公司聚餐,喝了点小酒,ZhangSan得找一个代驾,于是代理类来了。
/**
* 代理类
*/
public class DiDiDriver implements IDrive {
//持有IDrive对象的引用
private IDrive iDrive;
public DiDiDriver(IDrive iDrive) {
this.iDrive = iDrive;
}
@Override
public void drive() {
iDrive.drive();
}
}
这里的代理类DiDiDriver持有IDrive对象的引用,调用时需要传递一个被代理的对象进来,最后我们看下测试类:
public class Test {
public static void main(String[] args) {
//定义ZhangSan对象
IDrive zhangsan = new ZhangSan();
//DiDi代驾
IDrive diDiDriver = new DiDiDriver(zhangsan);
diDiDriver.drive();
}
}
打印结果:开车中...
到这里我们思考一个问题,代理类还可以代理其他人吗?当然是可以的,只需要再定义一个类LiSi实现IDrive接口,然后再修改调用的代码,也可以实现。
我们在前面说过代理模式还有一种是动态代理,静态代理是在编译时就已经确定是为谁代理,而动态代理是根据运行时决定。在Java中给我们提供了一个动态代理接口
InvocationHandler,实现该接口并重写invoke方法。
public class DynamicProxy implements InvocationHandler {
//被代理类的引用
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用被代理类对象的方法
Object result = method.invoke(object, args);
return result;
}
}
最后我们看下动态代理的测试类怎么写
public class Test {
public static void main(String[] args) {
//定义ZhangSan对象
IDrive zhangsan = new ZhangSan();
//构造一个动态代理
DynamicProxy dynamicProxy = new DynamicProxy(zhangsan);
//获取被代理类ZhangSan的ClassLoader
ClassLoader classLoader = zhangsan.getClass().getClassLoader();
//动态创建一个DiDi代驾
IDrive diDiDriver = (IDrive) Proxy.newProxyInstance(classLoader, new Class[]{IDrive.class}, dynamicProxy);
diDiDriver.drive();
}
}
结果跟静态代理是一样的,动态代理可以通过一个代理类来代理多个被代理类,达到代理类和被代理类解耦的目的。静态代理只能为给定接口下的实现类做代理,如果接口不同,则需要创建新的代理类。
三、代理模式和装饰模式的区别
代理模式和装饰模式比较类似,但我们需要知道它们本质的区别:装饰模式是在具体的组件实现类中扩展了自己的方法,最后调用的是装饰者自己的方法;代理模式,顾名思义,只是一个代理,不会有扩展方法,同时调用的也是被代理类的方法。