概述
在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。
在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大(如视频或大图像等),其下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。
定义:
为其他对象提供一种代理以控制对这个对象的访问
举个栗子
我在外地工作,想念家乡的特产,但是由于我太忙了只能托人去购买。
抽象主题类
抽象主题类具有真实主题类和代理的共同接口方法,共同接口方法就是购买。
public interface IShop {
void buy();
}
真实主题类
这个购买者也就是我,实现了IShop接口提供的buy()方法
public class Buyer implements IShop {
@Override
public void buy() {
System.out.println("购买");
}
}
代理类
我找的代理同样也要实现IShop接口,并且要持有被代理者,在buy()方法中调用了被代理者的buy()方法
public class Purchasing implements IShop {
private IShop mShop;
public Purchasing(IShop shop){
mShop = shop;
}
@Override
public void buy() {
mShop.buy();
}
}
客户端类
public class Client {
public static void main(String[] args) {
IShop buyer = new Buyer();
IShop purchasing = new Purchasing(buyer);
purchasing.buy();
}
}
客户端类的代码就是代理类包含了真实主题类(被代理者),最终调用的都是真实主题类(被代理者)实现的方法。在上面的例子就是Buyer类的buy()方法,所以运行的结果就是“购买”。
动态代理的实现
从编码的角度来说,代理模式分为静态代理和动态代理。上面的例子是静态代理,在代码运行前就已经存在了代理类的class编译文件;而动态代理则是在代码运行时通过反射来动态地生成代理类的对象,并确定到底来代理谁,代理谁将会在代码运行时决定。java提供了动态代理接口InvocationHandler,实现该接口需要重写invoke()方法。下面我们在上面静态代理的例子上做修改。首先创建动态代理类。
public class DynamicPurchasing implements InvocationHandler {
private Object object;
public DynamicPurchasing(Object object){
this.object = object;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object result = method.invoke(object,objects);
if (method.getName().equals("buy")){
System.out.println("买买买");
}
return result;
}
}
在动态代理类中我们声明一个Object的引用,该引用指向被代理类,我们调用被代理类的具体方法在invoke()方法中执行。接下来我们修改客户端类代码:
public class Client {
public static void main(String[] args) {
IShop buyer = new Buyer();
//创建动态代理
DynamicPurchasing mDynamicPurchasing = new DynamicPurchasing(buyer);
ClassLoader loader = buyer.getClass().getClassLoader();
//动态创建代理类
IShop purchasing = (IShop) Proxy.newProxyInstance(loader,new Class[]{IShop.class},mDynamicPurchasing);
purchasing.buy();
}
}
调用Proxy.newProxyInstance()来生成动态代理类,调用purchasing的buy方法会调用DynamicPurchasing的invoke方法。
代理模式的类型和优点
代理模式从编码的角度来说可以分为静态代理和动态代理,而从适用范围来讲则可分为以下4种类型。
- 远程代理:为一个对象在不同的地址空间提供布局代表,这样系统可以将Servier部分的实现隐藏
- 虚拟代理:使用一个代理对象表示一个十分耗费资源的对象并在真正需要时才创建。
- 安全代理:用来控制真实对象访问时的权限。一般用于真实对象有不同的访问权限时。
- 智能指引:当调用真实的对象时,代理处理另外一些事,比如计算真实对象的引用计数,当该对象没有引用时,可以自动释放它;或者访问一个实际对象时,检查是否已经能够锁定它,以确保其他对象不能改变它。
- 代理模式的优点主要有以下几点
- 真实主题类就是实现实际的业务逻辑,不用关心其他非本职的工作。
- 真实主题类随时都会发生变化;但是因为它实现了公共的接口,所以代理类可以不做任何修改就能够使用。
借鉴(《Android进阶之光》刘望舒)