代理模式,为其他对象提供一种代理来控制这个对象的访问。生活中有很多这样子的例子。如果你喜欢购物,相信你对代购不会陌生。那么你去找到代购,代购为你买回来商品这样行为方式就 是一种代理模式。如果你喜欢玩游戏你又不想自己亲手或者技术有限,你去找到代练,让他们为你上分,升级,这样的行为方式也是一种代理模式。在代理模式下涉及到的角色模型:
- 抽象行为模型:通常使用接口实现。由代理类跟代理角色共同实现,定义了代理的目的行为。
- 代理模型:需要实现抽象行为。充当代理角色,控制真实实施角色,对抽象行为增加额外补充,不执行抽象行为。
- 具体模型:实现抽象行为。被代理角色,是真正的抽象行为执行者。
以找代购购买iPhone手机为一个小例子。首先是抽象行为,就是去买iPhone:
public interface Subject {
//目的
void buyIphoneX();
}
具体模型:
public class RealSubject implements Subject {
@Override
public void buyIphoneX() {
Log.d("ProxySubject", "buyIphoneX: 我去买iPhone了");
}
}
具体模型的内容很简单,就是执行具体的抽象行为,它就只有这一个目的。代理类:
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject() {
subject = new RealSubject();
}
@Override
public void buyIphoneX() {
this.goToAmerica();
subject.buyIphoneX();
this.goBack();
}
private void goToAmerica() {
Log.d("ProxySubject", "goToAmerica: 去美国");
}
private void bargain() {
Log.d("ProxySubject", "bargain: 讲价");
}
private void goBack() {
Log.d("ProxySubject", "goBack: 返程");
}
}
可以看到代理类除了控制被代理角色去执行抽象行为外,还有其他的处理,像去到哪个地方买,怎么个买法,买完后怎么样。这些都是一些额外的处理。这些抽象行为额外的操作是根据具体情况增加的。使用场景:
Subject subject = new ProxySubject();
subject.buyIphoneX();
输出结果:
D/ProxySubject: goToAmerica: 去美国
D/ProxySubject: buyIphoneX: 我去买iPhone了
D/ProxySubject: goBack: 返程
OK,代理模式简单的例子就是这样了。这种方式一般称为静态代理模式,就是每个代理都是由具体类对象直接调用;而还有一种动态代理模式,则是通过反射间接的调用代理方法。动态代理角色模型跟静态代理差不多,只是代理模型发生了变化,它不是实现抽象行为模型接口了,而是实现InvocationHandler 这个专属接口。
public class DynamicProxy implements InvocationHandler {
//代理类
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
/**
* @param proxy
* @param method 代理方法
* @param args 代理参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
它的使用:
RealSubject r = new RealSubject();
DynamicProxy d = new DynamicProxy(r);
ClassLoader classLoader = r.getClass().getClassLoader();
Subject s = (Subject) Proxy.newProxyInstance(classLoader, r.getClass().getInterfaces(), d);
s.buyIphoneX();
跟静态模式看起来好像都没有什么不一样,但是静态代理模式里一个真实对象就要对应一个代理类,而真实代理往往会有很多的,不可能全部对应创建代理类。而使用动态代理就方便很多了。稍微改写:
/**
* 代理方法
* @param r 真实代理对象
*/
private void proxy(Subject r) {
DynamicProxy d = new DynamicProxy(r);
//获取classloader
ClassLoader classLoader = r.getClass().getClassLoader();
//获取代理对象
Subject s = (Subject) Proxy.newProxyInstance(classLoader, r.getClass().getInterfaces(), d);
s.buyIphoneX();
}
然后调用:
RealSubject r1 = new RealSubject();
RealSubject2 r2 = new RealSubject2();
RealSubject3 r3 = new RealSubject3();
proxy(r1);
proxy(r2);
proxy(r3);
这样即使有再多的真实代理,也只需要一个代理类就可以了。简单方便。
代理模式的分类
按照使用目的可以分为以下类别:
- 远程代理:为一个位于不同地址的空间对象提供一个局部代表对象。这个不同空间的地址可以是同一台机器或者不同机器 上。
- 虚拟代理:当需要创建一些消耗资源较多的对象时,可以首先创建代理对象,将真实对象的创建延迟。例如加载大图片。
- 保护代理:控制对一个对象的访问。
- 缓存代理:为一个目标操作的结果提供临时的存储空间。
- 同步代理:使多个用户能够使用同一个对象而且没有冲突。
- 智能引用代理:当一个对象被引用时,提供一些额外的操作。
上述分类中远程代理,虚拟代理,保护代理跟智能引用代理是使用较多的分类。
最后代理模式的优劣
优点
- 真实代理与调用对象之前存在代理类,各个角色职责清晰,降低程序耦合度;
- 代理类在运行时才加载真实代理,一定程度上减少资源开销,提升效率;
缺点:
- 刚刚说过,真实代理与调用对象之前存在代理类,在降低耦合度的同时也会增加结构的复杂度;
- 相比跨过代理类直接泛起请求,代理模式处理速度会变慢;