前言
代理模式(Proxy Pattern)也称为委托模式。那么代理模式是啥呢?其实代理在我们日常生活中也并不少见,对于程序员来说最常接触的莫过于代理上网了,连上代理服务器地址,就可以轻松畅游全世界的网络;还有每天吃饭时赶进度是常事,叫公司的同事帮忙买饭也是一种代理;如果你碰到辞职老板不给你发工资,那么你还得请个律师帮你打官司,这也是一种代理。总而言之,也许你并不留意,但是代理的确是无处不在,现实生活中如此,我们的Code世界里也是如此!
定义
为其他对象提供一种代理以控制对这个对象的访问。
使用场景
当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
简单示例
Android中的代理模式十分常见,根据代码来归类。主要分为静态代理模式和动态代理模式。
1. 静态代理
我们举个简单的例子,小民以前在公司上班时,就遇到过被老板拖欠工资甚至克扣工资的情况,这种情况下小民还是通过法律途径来解决问题,一旦小民选择了走法律途径解决该纠纷,那么不可避免地就需要请一个律师来作为自己的诉讼代理人,我们将诉讼的流程抽象在一个接口类中。
- 诉讼接口类
public interface ILawsuit {
// 提交申请
void submit();
// 进行举证
void burden();
// 开始辩护
void defend();
// 诉讼完成
void finish();
}
4个方法都很简单,都是诉讼的一般流程。
- 具体诉讼人
public class XiaoMin implements ILawsuit {
@Override
public void submit() {
// 老板欠小民工资 小民只好申请仲裁
System.out.println("老板拖欠工资!特此申请仲裁!");
}
@Override
public void burden() {
// 小民证据充足,不怕告不赢
System.out.println("这是合同书和过去一年的银行工资流水!");
}
@Override
public void defend() {
// 铁证如山,辩护也没什么好说的
System.out.println("证据确凿!不需要再说什么了!");
}
@Override
public void finish() {
// 结果也是肯定的,必赢
System.out.println("诉讼成功!判决老板即日起七天内结算工资");
}
}
如上所述,该类实现ILawsuit并对其中4个方法作出具体的实现逻辑,逻辑很简单,都只是输出一段话而已,当然,小民自己是不会去打官司的,于是小民请个律师代替自己进行诉讼。
- 代理律师
public class Lawyer implements ILawsuit {
private ILawsuit mLawsuit;// 持有一个具体被代理者的引用
public Lawyer(ILawsuit lawsuit) {
mLawsuit = lawsuit;
}
@Override
public void submit() {
mLawsuit.submit();
}
@Override
public void burden() {
mLawsuit.burden();
}
@Override
public void defend() {
mLawsuit.defend();
}
@Override
public void finish() {
mLawsuit.finish();
}
- 客户类中的调用执行过程
public class Client {
public static void main(String[] args) {
// 构造一个小民……
ILawsuit xiaomin = new XiaoMin();
// 构造一个代理律师并将小民作为构造参数传递进去
ILawsuit lawyer = new Lawyer(xiaomin);
// 律师提交诉讼申请
lawyer.submit();
// 律师进行举证
lawyer.burden();
// 律师代替小民进行辩护
lawyer.defend();
// 完成诉讼
lawyer.finish();
}
}
运行结果很简单这里不再给出,大家可以看到,其实代理模式也很简单,其主要还是一种委托机制,真实对象将方法的执行委托给代理对象,而且委托得干净利落毫不做作,这也是为什么代理模式也称为委托模式的原因,相信大家不难理解。除此之外,大家其实可以继续发散思维,其实我们的代理类完全可以代理多个被代理类,就像上面的例子一样,一个律师可以代理多个人打官司,这是没有任何问题的,而具体到底是代理的哪个人,这就要看代理类中所持有的实际对象类型。
2. 动态代理
静态代理如上述示例那样,代理者的代码由程序员自己或通过一些自动化工具生成固定的代码再对其进行编译,也就是说在我们的代码运行前代理类的class编译文件就已存在;而动态代理则与静态代理相反,通过反射机制动态地生成代理者的对象,也就是说我们在code阶段压根就不需要知道代理谁,代理谁我们将会在执行阶段决定。而Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke。
public class DynamicProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
在这里,我们主要通过invoke方法来调用具体的被代理方法,也就是真实的方法。动态代理可以使我们的代码逻辑更简洁,不过在这之前我们得首先完善动态代理类。
public class DynamicProxy implements InvocationHandler {
private Object obj;// 被代理的类引用
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用被代理类对象的方法
Object result = method.invoke(obj, args);
return result;
}
}
如上代码所述,我们声明一个Object的引用,该引用将指向被代理类,而我们调用被代理类的具体方法则在invoke方法中执行,是不是很简洁?也就是说我们原来由代理类所做的工作现在由InvocationHandler来处理,不再需要关心到底代理谁。下面我们修改客户类的逻辑。
public static void main(String[] args) {
// 构造一个小民……
ILawsuit xiaomin = new XiaoMin();
// 构造一个动态代理
DynamicProxy proxy = new DynamicProxy(xiaomin);
// 获取被代理类小民的ClassLoader
ClassLoader loader = xiaomin.getClass().getClassLoader();
// 动态构造一个代理者律师
ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(loader, new Class[]{ILawsuit.class}, proxy);
// 律师提交诉讼申请
lawyer.submit();
// 律师进行举证
lawyer.burden();
// 律师代替小民进行辩护
lawyer.defend();
// 完成诉讼
lawyer.finish();
}
总结
代理模式应用广泛。我们讲到的其他形式的结构型模式中,你都可以看到代理模式的影子,有些模式单独作为一种设计模式,倒不如说是对代理模式的一种针对性优化。而且代理模式几乎没有什么缺点可言,它是细分化至很小的一种模式,要真的说一个缺点,那么就是所有设计模式的通病:对类的增加,不过在这种孰优孰劣的局势下,就算对类的稍微增加又何妨呢?
网友评论