1 定义
代理模式(Proxy Pattern)又名委托模式,属于结构型设计模式之一,它主要用于对现有一个对象提供一个代理对象,并由代理对象控制原有对象的访问或访问前后增加相应逻辑。比如某些情况下,一个对象不适合直接引用另一个对象,而可以通过在引用和被引用之间加入一个代理对象来作为一个桥梁连接调用。例如现实场景中,某品牌官方厂家可以卖商品,而它拥有着一些专卖店,我们想买它的商品时一般不会直接跑到厂家那里买,而是通过它的专卖店去买,而且说不定专卖店还常有优惠活动,这里的专卖店就是起到一个代理的作用,其实专卖店卖出的商品最终也是从厂家出货。
2 实现
代理模式一般包含了3个角色,分别是:
- 抽象主题(Subject),就是真实主题的抽象类,它也是真实主题与代理的共同父类。例如上述定义举例的卖商品的商家统称。
- 真实主题(RealSubject),就是被代理的真实主题。例如上述定义举例的官方厂家。
- 代理(Proxy),在静态代码中持有对真实主题的引用,并拥有以真实主题一样的方法。例如上述定义举例的专卖店。
在使用上代理模式又可分为:
- 静态代理模式,程序设计上需要创建一个代理类(Proxy),且该类内方法要拥有以真实主题一样的方法。
- 动态代理模式,不需要创建代理类,代理对象由JDK在运行时根据反射机制动态创建。
2.1 静态代理
抽象主题类,它是卖商品的商家统称:
public abstract class Shop {
protected abstract void sale();
}
真实主题类,它继承于抽象主题类,是具体的商家,这里是官方厂家:
public class OfficialShop extends Shop {
@Override
protected void sale() {
System.out.println("官方厂家提供货源");
}
}
代理类,它也是继承于抽象主题类,且拥有着一个抽象主题类对象,它就是专卖店也叫厂家代理点:
public class OfficialShopProxy extends Shop {
private Shop mShop;
public OfficialShopProxy(Shop shop) {
mShop = shop;
}
@Override
protected void sale() {
System.out.println("欢迎光临XXX专卖店");
mShop.salel();
System.out.println("代理点办促销活动,还可赠送礼品");
}
}
客户端:
Shop officialShop = new OfficialShop();
Shop officialShopProxy = new OfficialShopProxy(officialShop);
officialShopProxy.sale();
输出结果:
欢迎光临XXX专卖店
官方厂家提供货源
代理点办促销活动,还可赠送礼品
2.2 动态代理
当业务变得复杂时,目标主题业务需要进行扩展,即抽象主题和真实主题中方法增加时,代理类就得一起增加相应的方法进行代理。这时就会显得静态代理代码拥挤,且维护工作量太大。而动态代理则不同,它的代理对象由JDK在运行时根据反射机制动态创建。
JDK中动态代理主要依赖接口java.lang.reflect.InvocationHandler 和 类java.lang.reflect.Proxy来进行实现。
InvocationHandler接口是代理实例的调用处理程序实现的接口,当执行目标对象的方法时,会触发它的invoke方法。
Proxy类提供了用于创建动态代理类和实例的静态方法,其关键方法就是newProxyInstance,它返回了代理类的实例,其参数有:
ClassLoader loader:被代理的目标对象的类加载器
Class<?>[] interfaces:被代理的目标对象实现的接口的类型
InvocationHandler:继承于InvocationHandler接口的动态处理器
来通过代码看看动态代理的实现:
抽象主题类,它是卖商品的商家统称,动态代理要求必须是接口:
public interface IShop {
void sale();
void repair();
void partExchange();
}
真实主题类,它继承于抽象主题类,是具体的商家,这里是官方厂家:
public class OfficialShop implements IShop {
@Override
public void sale() {
System.out.println("官方厂家提供货源");
}
@Override
public void repair() {
System.out.println("官方厂家维修商品");
}
@Override
public void partExchange() {
System.out.println("官方厂家对商品以旧换新");
}
}
通用的动态处理器,继承于InvocationHandler接口,在invoke方法中完成所有代理工作:
public class DynamicProxyHandler implements InvocationHandler {
private Object mObject;
public DynamicProxyHandler(Object object) {
this.mObject = object;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
if (mObject instanceof IShop) {
if ("sale".equalsIgnoreCase(method.getName())) {
System.out.println("欢迎光临XXX专卖店");
Object result = method.invoke(mObject, objects);
System.out.println("代理点办促销活动,还可赠送礼品");
return result;
} else if ("repair".equalsIgnoreCase(method.getName())) {
System.out.println("专卖店验证购买日期");
Object result = method.invoke(mObject, objects);
return result;
} else if ("partExchange".equalsIgnoreCase(method.getName())) {
System.out.println("专卖店评估旧商品价值");
Object result = method.invoke(mObject, objects);
return result;
}
}
return null;
}
}
客户端,通过Proxy.newProxyInstance方法获取代理对象:
IShop officialShop = new OfficialShop();
IShop shop = (IShop) Proxy.newProxyInstance(IShop.class.getClassLoader(), new Class[]{IShop.class}, new DynamicProxyHandler(officialShop));
shop.sale();
shop.repair();
shop.partExchange();
输出结果:
欢迎光临XXX专卖店
官方厂家提供货源
代理点办促销活动,可赠送礼品
专卖店验证购买日期
官方厂家维修商品
专卖店评估旧商品价值
官方厂家对商品以旧换新
3 总结
代理模式其优点在于代理类起到调用者和目标主题业务的桥梁连接调用,目标主题在实现业务逻辑时也不需要过多关心其它非本职的事务,因为后期代理类会代它完成这些事务,这样角色的职责也清晰。而且可以做到不修改目标主题的前提下来增加额外的功能扩展,这样代码也符合开闭原则。
静态代理中,一个代理对象只服务于一种类型的目标主题对象,适合于业务独立且功能较少的情况下。如果主题对象存在很多方法时,代理对象就也得每一种方法都要进行代理,后期如果增加方法,抽象主题类、真实主题类和和代理类都要一同增加,这样就使得增加了维护工作量和复杂度。
动态代理可以使用通用的动态处理器来代理多种类型的目标主题对象,它的代理对象由JDK在运行时根据反射机制动态创建,是依赖接口java.lang.reflect.InvocationHandler 和 类java.lang.reflect.Proxy来工作。因为它不会随着业务接口变化而进行代理的扩展,所以大大减少了代码量,但是美中不足的是它始终需要对接口进行代理,即目标主题对象必须存在着一个抽象主题接口才能完成动态代理。