代理模式的类图如下所示:
客户端想调用的是RealSubject,由于某种考虑或原因,只能直接访问到ProxySubject,再由ProxySubject去调用RealSubject,这就完成了一次代理的活动。
代理模式的时序图如下:
从上面可以看出,ProxySubject不仅可以完成对RealSubject的调用,在调用前后还可以完成一些事情,这就是代理模式的优点。
代理模式按照使用的分类,可以分为以下几类:
远程代理:为一个不同地址空间的对象提供一个局域代表对象。
虚拟代理:根据需求创建一个资源消耗较大的对象,使得对象在使用时才被真正地使用。
Copy-on-Write代理:虚拟代理的一种,把复制行为推迟到真正需要时再去执行。
保护代理:控制对一个对象的访问,可以对不同用户提供不同权限。
Cache代理:为某一目标的操作结果提供临时的存储空间,使得多个客户端可以共享这些结果。
同步化代理:使得多个用户同时使用一个目标而没有冲突。
智能引用代理:引用一个对象时提供一些额外的操作,如记录被调用的次数等。
Java类库中有三个类直接支持代理模式:Proxy,InvocationHandler和Method。
下面是在List加上代理,在添加元素的前后打印一些信息。
public class VectorProxy implements InvocationHandler{ private Object proxyobj; public VectorProxy(Object obj){ proxyobj = obj; } public static Object factory(Object obj){ Class cls = obj.getClass(); return Proxy.newProxyInstance( cls.getClassLoader(), cls.getInterfaces(), new VectorProxy(obj) ); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{ System.out.println("before calling " + method); if (args != null){ for (int i=0; i<args.length; i++){ System.out.println(args[i] + ""); } } Object o = method.invoke(proxyobj, args); System.out.println("after calling " + method); return o; } public static void main(String[] args){ List v = null; v = (List) factory(new Vector(10)); v.add("New"); v.add("York"); } }
打印出的信息如下:
before calling public abstract boolean java.util.List.add(java.lang.Object) New after calling public abstract boolean java.util.List.add(java.lang.Object) before calling public abstract boolean java.util.List.add(java.lang.Object) York after calling public abstract boolean java.util.List.add(java.lang.Object)
一个网站可以为顾客提供股票的持有情况,从而判断是否是大量收购或抛售的情况,这是信息是很有用的,该网站属于计费网站。这里有两种情形是能使用代理模式的,一是对客户的身份进行检查,第二是对用户的使用情况进行统计,方便计费。
上面提到的代理模式的种类,这里就可以对应上保护代理和智能引用代理两种。
负责身份验证和查询次数记录功能类应该是分开处理,对应与上面类图中的AccessValidator和UsageLogger,统一于Proxy中进行调用。
代理类Proxy 的示例代码为:
public class Proxy implements Searcher { private RealSearcher searcher; private UsageLogger usageLogger; private AccessValidator accessValidator; public Proxy(){ searcher = new RealSearcher(); } public String doSearch(String userId, String keyValue){ if (checkAccess(userId)){ String result = searcher.doSearch(null, keyValue); logUsage(userId); return result; } else{ return null; } } private boolean checkAccess(String userId){ accessValidator = new AccessValidator(); return accessValidator.vaidateUser(userId); } private void logUsage(String userId){ UsageLogger logger = new UsageLogger(); logger.setUserId(userId); logger.save(); } }
负责身份校验的AccessValidator的示例代码:
public class AccessValidator{ public boolean vaidateUser(String userId){ if (userId.equals("Admin")){ return true; } else{ return false; } } }
负责查询次数记录的UsageLogger的示例代码:
public class UsageLogger{ private String userId; public void setUserId(String userId){ this.userId = userId; } public void save(){ String sql = "INSERT INTO USAGE_TABLE (user_id) " + " VALUES(" + userId + ")"; //execute this SQL statement } public void save(String userId){ this.userId = userId; save(); } }
实际的查询处理类RealSearcher的示例代码:
class RealSearcher implements Searcher{ public RealSearcher(){ } public String doSearch(String userId, String keyValue){ String sql = "SELECT * FROM data_table WHERE key_col = '" + keyValue + "'"; //execute this SQL Statement and concatenate a result string return "result set"; } }
系统加载一个较为耗时的模块时,首先显示“正在加载”的信息,同时在加载模块,模块加载完后去掉加载信息。这里可以使用代理模式。
加载图片时,先加载一个分辨率较低的图片,等到真正的图片加载完后再进行显示,也可以使用代理模式去实现。