1.什么是代理模式
代理模式(Proxy)为另一个对象提供一个替身或占位符以控制该对象的访问(Provide asurrogate or placeholder for another object to control access to it),简而言之就是用一
个对象来代表另一个对象。
2.代理模式的作用
为其它对象提供一种代理来控制该对象的访问,在某些情况下,一个客户不想或不可以直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。
因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ? 你有想过限制访问某个对象,
也就是说,提供一组方法给普通用户,特别方法给管理员用户?以上两种需求都非常类似,并且都需要解决一个更大的问题:你如何提供一致的接口给某个对象让它可以改变其内部
功能,或者是从来不存在的功能? 可以通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,即代理对象。它可以在客户端和目标对象之间
起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。
代理模式一般涉及三个对象:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供和真实对象相同的接口以便在任何时候都能替代真实对象。同时,代理对象在操作真实对象
时可以附加其他操作,相当于对真实对象进行封装;
真实角色:代理对象所代表的真实对象,是我们最终要引用的对象。
3.代理模式实例
抽象角色:
abstract public class Subject{ abstract public void Request(); }
真实角色:实现了Subject的Request方法
public class RealSubject extends Subject{ public RealSubject(){} public void Request(){ System.out.println("from real subject."); } }
代理角色:
public class ProxySubject extends Subject{ private Realsubject rs; public ProxySubject(){} public void Request(){ preRequest(); if(rs == NULL){ rs = new Realsubject(); } rs.Request(); postRequest(); } }
客户端调用:
public static void main(){ Subject ps = new ProxySubject(); ps.Request(); }
由此我们看到,客户端实际调用的是RealSubject的Request()方法,现在用ProxySubject 来代理RealSubject类,同样达到目的,并且还封装了preRequest()和proRequest()方
法,可以处理一些其他问题。一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代理哪个真实主题角色,是由场景类决定的,最简单的情况就是一个主题类一个代理类,这是最简洁的代理模式。在通常情况下,一个接口只需要一个代理类就可以了,具体代理哪个实现类由高层模块来决定,也就是在代理类的构造函数中传递被代理者。
我们来看一个例子:
interface IGamePlayer{ public void login(); public void killBoss(); } class GamePlayer implements IGamePlayer{ String name = ""; public GamePlayer(String name){ this.name = name; } @Override public void login() { System.out.println(this.name + "has login."); } @Override public void killBoss() { System.out.println(this.name + "has kill a boss"); } } class Proxy implements IGamePlayer{ private IGamePlayer gp; public Proxy(IGamePlayer gp){ this.gp = gp; } @Override public void login() { this.gp.login(); } @Override public void killBoss() { this.gp.killBoss(); } } public class ProxyPattern{ public static void main(String[] args) { IGamePlayer gp = new GamePlayer("zhangsan"); IGamePlayer proxy = new Proxy(gp); proxy.login(); proxy.killBoss(); } }
普通代理:客户端只能访问代理角色,而不能访问真实存在角色,调用者根本不知道真实角色的存在。
此时只需要做如下处理:
class GamePlayer implements IGamePlayer{ String name = ""; public GamePlayer(Proxy proxy,String name){ if(proxy == null) throw new Exception("不能创建角色"); else this.name = name; } @Override public void login() { System.out.println(this.name + "has login."); } @Override public void killBoss() { System.out.println(this.name + "has kill a boss"); } } class Proxy implements IGamePlayer{ private IGamePlayer gp = null; public Proxy(String name){ try{ gp = new GamePlayer(this, name); }catch(Exception){ } } @Override public void login() { this.gp.login(); } @Override public void killBoss() { this.gp.killBoss(); } }
在该模式下,调用者只需要知道代理而不用知道真实的角色是谁,在场景中避免了new一个真实角色。
强制代理:
代码如下:
interface IGamePlayer{ public void login(); public void killBoss(); public Proxy getProxy(); } class GamePlayer implements IGamePlayer{ private Proxy proxy = null; String name = ""; public GamePlayer(String name){ this.name = name; } @Override public void login() { if(isProxy()) System.out.println(this.name + "has login."); else System.out.println("use proxy!"); } @Override public void killBoss() { if(isProxy()) System.out.println(this.name + "has kill a boss"); else System.out.println("use proxy"); } public Proxy getProxy(){ this.proxy = new Proxy(this); return this.proxy; } private boolean isProxy(){ if(this.proxy == null) return false; return true; } } class Proxy implements IGamePlayer{ private IGamePlayer gp = null; public Proxy(IGamePlayer gp){ this.gp = gp; } @Override public void login() { this.gp.login(); } @Override public void killBoss() { this.gp.killBoss(); } @Override public Proxy getProxy() { return this; } }
如果我们直接创建代理来访问被代理对象:
public class ProxyPattern{ public static void main(String[] args) { IGamePlayer gp = new GamePlayer("zhangsan"); IGamePlayer proxy = new Proxy(gp); proxy.login(); proxy.killBoss(); } }
会输出两个“use proxy“。
如果我们直接使用真实对象:
public class ProxyPattern{ public static void main(String[] args) { IGamePlayer gp = new GamePlayer("zhangsan"); gp.login(); gp.killBoss(); } }
同样会输出两个“use proxy“。
正确的方法是利用真实对象产生代理:
public class ProxyPattern{ public static void main(String[] args) { IGamePlayer gp = new GamePlayer("zhangsan"); IGamePlayer proxy = gp.getProxy(); proxy.login(); proxy.killBoss(); } }
强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色,高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
动态代理用到了反射,待续:
http://www.cnblogs.com/cbf4life/archive/2010/01/27/1657438.html
4.代理模式的种类
①远程代理:为一个位于不同的地址空间的对象提供一个局域代表对象,这个不同的地址空间可以是在本机器中,也可以是在另一个机器中,远程代理也叫大使。好处是系统可
以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部分的网络通信工作。
②虚拟代理(图片延迟加载的例子):根据需要创建一个资源消耗大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以先创建一个消
耗相对较小的对象来表示,然后在必要的时候才将被代理的对象加载,代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源时,虚拟代理的好处就非常明
显。
③Copy-on-write代理:虚拟代理的一种,把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理
可以让这个操作延迟,只有对象被用到的时候才被克隆。
④智能引用代理(Smart Reference):当一个对象被引用时,提供一些额外操作,比如将对象调用的次数记录下来,取代了简单的指针,它在访问对象时执行一些附加操作。
⑤保护代理(Protection Proxy)控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。