结构模式--之--代理模式
代理模式是对象的结构模式,代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
按照使用目的来划分,代理有以下几种:
1.远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。优点 是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的,而不是远程的而代理对象承担了大部分的网络通信工作。缺点是客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
2.虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的时候,虚拟代理的优点就非常明显。
3.Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
4.保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限
5.Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
6.防火墙(Firewall)代理:保护目标,不让恶意用户接近
7.同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
8.智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
在所有种类的代理模式中,虚拟代理,远程代理,智能引用代理和保持代理是最为常见的代理模式。
代理模式的角色有:
1.抽象主题角色:声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以使用代理主题。
2.代理主题(Proxy)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象,代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主体,控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象),代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯地将调用传递给真实主题对象。
3.真实主题角色:定义了代理角色所代表的真实对象。
示例性代码如下:
1 public class ProxyTest { 2 public static void main(String[] args) { 3 Subject subject = new ProxySubject(); 4 subject.request(); 5 } 6 } 7 8 9 //抽象主题角色 10 abstract class Subject{ 11 //抽象请求方法 12 abstract public void request(); 13 } 14 15 //具体角色 16 class RealSubject extends Subject{ 17 //实现真实的请求方法 18 @Override 19 public void request() { 20 System.out.println("From real subject"); 21 } 22 23 } 24 25 //代理角色,它除了将所有的请求原封不动地委派给真实主题角色外,还在委派前和后执行了一些操作 26 class ProxySubject extends Subject{ 27 28 private RealSubject realSubject; 29 30 //请求方法 31 @Override 32 public void request() { 33 preRequest(); 34 if(null == realSubject){ 35 realSubject = new RealSubject(); 36 } 37 realSubject.request(); 38 postRequest(); 39 } 40 //请求前 41 public void preRequest(){ 42 System.out.println("Before Request"); 43 } 44 //请求后 45 public void postRequest(){ 46 System.out.println("After Request"); 47 } 48 }
从JDK3后,Java语言通过在java.lang.reflect库中提供下面三个类直接支持代理模式:Proxy,InvocationHandler和Method.其中Proxy类使得设计师能够在运行时间创建代理对象,当系统有了一个代理对象后,对原对象的方法调用会首先被分派给一个调用处理处理器(InvocationHandler).程序可以在调用处理器invoke()方法中截获这个调用,进行额外的操作。显然Java所提供的这一支持是建立在反射之上的。
设计师可以按以下步骤创建动态代理对象:
1.指明一系列的接口来创建一个代理对象
2.创建一个调用处理器(InvocationHandler)对象
3.将这个代理指定为某个其他对象的代理对象
4.在调用处理器的invoke()方法中采取代理,一方面将调用传递给真实对象,另一方面执行各种需要做的操作。
以下以一个例子来说明使用动态代理:为一个Vector对象提供一个代理对象,当Vector的任何方法被调用之前和调用之后,分别打印出两条信息,这表明代理对象能能力截获和控制这个Vector对像
1 public class DynamicProxy { 2 public static void main(String[] args) throws Exception{ 3 4 List list= (List)VectorProxy.factory(new Vector()); 5 list.add("one"); 6 list.add("two"); 7 8 } 9 } 10 11 class VectorProxy implements InvocationHandler{ 12 13 private Object proxyobj; 14 15 public VectorProxy(Object obj){ 16 this.proxyobj = obj; 17 } 18 19 //调用某个方法 20 @Override 21 public Object invoke(Object proxy, Method method, Object[] args) 22 throws Throwable { 23 System.out.println("Before calling"+method); 24 Object o = method.invoke(proxyobj, args); 25 System.out.println("After calling"+method); 26 return o; 27 } 28 public static Object factory(Object obj) throws Exception{ 29 Class cls = obj.getClass(); 30 return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),new VectorProxy(obj)); 31 32 } 33 }