把时间用在思考上是最能节省时间的事情。 — 卡曾斯
写在前面
本篇讲解代理模式,分静态代理和动态代理两部分,由浅入深更好的理解代理模式。
代理模式又被称为委托模式,在现实生活中类似代理模式这种场景有很多,比如请律师打官司,代购,代理上网等。
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。
抽象主题类(Subject):声明真实主题与代理的公共的接口方法。
真实主题类(RealSubject):代理类所代表的真实主题,客户端通过代理类间接调用真实主题类的方法。
代理类(Proxy):持有对真实主题类的引用,在其所实现的接口方法中调用真实主题类相应的接口方法执行。
静态代理
通过一个例子来认识代理模式,我看好了一双球鞋,想托代购小芳帮我买,而我只需要给她钱就好,至于她怎么买去哪买我并不关心,最终能把鞋子送到我手里就好了。
我和代购小芳都具有"买"的行为,而代购小芳代替我去买,所以我和代购小芳不同的是代购小芳有买的过程,我却没有。
1.抽象主题类
我和代购小芳都具有“买”的行为,我是真实主题类,代购小芳是代理类,所以要声明真实主题与代理类的公共接口方法。
/**
* 抽象主题类
*/
public interface Subject {
void buy(String goods);
}
2.真实主题类
我才是真正要买这双鞋子的人,然而我也具有“买”的行为,所以我要实现抽象主题类,实现buy()方法。
/**
* 真实主题类
*/
public class RealSubject implements Subject {
@Override
public void buy(String goods) {
Log.d("RealSubject", "哈哈哈哈,好开心,我买到了喜欢的" + goods);
}
}
3.代理类
代购小芳是帮我买,也具有“买”的行为,而且还要将买到的鞋子给我,所以代购小芳也要实现抽象主题类,实现buy()方法。
/**
* 代理类
*/
public class Proxy implements Subject{
private Subject mSubject;
public Proxy(Subject subject) {
mSubject = subject;
}
@Override
public void buy(String goods) {
// 代购小芳代替我买,买到后把鞋子给我
mSubject.buy(goods);
}
}
4.代购
现在就让代购小芳正式的帮我买鞋子了。
/**
* 客户端
*/
public class Client {
public Client() {
// 这是我
Subject realSubject = new RealSubject();
// 将我作为参数实例化代购小芳
Subject proxy = new Proxy(realSubject);
// 代购小芳替我买球鞋了
proxy.buy("好看的球鞋");
}
}
动态代理
从上面静态代理的例子中可以看出,代购小芳只为我一个人买东西,这一趟代购不赔钱就谢天谢地了,代购小芳作为一名职业代购不可能只给我一个人买东西, 她还会帮许多人购买商品赚取利润。那么如何解决这个问题呢,动态代理横空出世了。
静态代理在编译期间就已经决定了代理谁,动态代理在编译期间不会决定代理谁,而是在代码运行时通过反射动态的生成代理类的对象,Java提供了动态代理的接口InvocationHandler,实现这个接口需要重写invoke()方法。
1.代理类
现在让我们来修改代理类,让代购小芳不只再为我自己买东西。
/**
* 动态代理类
*/
public class InvocationProxy implements InvocationHandler {
private Subject mSubject;
public InvocationProxy(Subject subject) {
mSubject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d("invoke", "成功购买到所需商品 = " + args[0]);
return method.invoke(mSubject, args);
}
}
2.代购
代购小芳现在不可以为我代购,还能为别人代购,那么先以为我代购为例,为其他人代购的实现方式也一样。
/**
* 客户端
*/
public class InvocationClient {
public InvocationClient() {
// 这是我
Subject realSubject = new RealSubject();
// 创建动态代理类
InvocationHandler proxy = new InvocationProxy(realSubject);
// 调用Proxy.newProxyInstance()生成动态代理类
Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), new Class[]{Subject.class}, proxy);
subject.buy("好看的球鞋");
}
}
总结
代理模式从编码角度来说分为静态代理和动态代理,从适用范围来说分为远程代理,虚拟代理,安全代理和智能指引四种类型,分别如下:
- 远程代理:为一个对象在不同的地址空间提供局部代表,这样系统能够将Server部分的实现隐藏。
- 虚拟代理:使用一个代理对象表示一个十分耗费资源的对象并在需要时创建。
- 安全代理:用来控制真实对象访问时的权限,一般用于真实对象有不同的访问权限时。
- 智能指引:当调用真实对象时,代理在处理另一些事情,比如计算真实对象的引用计数,当该对象没有引用时,可以自动释放它。或者访问一个实际对象时,检查是否已经能够锁定它,以确保其他的对象不能改变它。
代理模式的优点:
- 真实主题类就是实现实际的业务逻辑,不用关心其他非本职工作。
- 真实主题类随时都会发生变化,但是因为它实现了公共的接口,所以代理类可以不做任何修改就能够使用。