代理模式定义
代理模式类图
代理模式包含如下角色
抽象角色:声明真实对象和代理对象的共同接口。
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
代理模式示例代码
//Subject.java public interface Subject { //定义一个方法 public void request(); } //RealSubject.java public class RealSubject implements Subject { //实现方法 public void request() { //业务逻辑处理 } } //Proxy.java public class Proxy implements Subject { //要代理哪个实现类 private Subject subject = null; //默认被代理者 public Proxy(){ this.subject = new Proxy(); } public Proxy(Subject _subject){ this.subject = _subject; } //通过构造函数传递代理者 public Proxy(Object...objects ){ } //实现接口中定义的方法 public void request() { this.before(); this.subject.request(); this.after(); } //预处理 private void before(){ //do something } //善后处理 private void after(){ //do something } } //Client.java public class Client { public static void main(String[] args) { Subject proxy = new RealSubject(); proxy.request(); } }
//Sourceable.java public interface Sourceable { public void method(); } //Source.java public class Source implements Sourceable { public void method() { System.out.println("the original method!"); } } //Proxy.java public class Proxy implements Sourceable { private Source source; public Proxy(){ super(); this.source = new Source(); } public void method() { before(); source.method(); atfer(); } private void atfer() { System.out.println("after proxy!"); } private void before() { System.out.println("before proxy!"); } } //ProxyTest.java public class ProxyTest { public static void main(String[] args) { Sourceable source = new Proxy(); source.method(); } }
代理模式示例:游戏代练帮忙打怪
uml图
代码
//IGamePlayer.java public interface IGamePlayer { //登录游戏 public void login(String user,String password); //杀怪,这是网络游戏的主要特色 public void killBoss(); //升级 public void upgrade(); } //GamePlayer.java public class GamePlayer implements IGamePlayer { private String name = ""; //通过构造函数传递名称 public GamePlayer(String _name){ this.name = _name; } //打怪,最期望的就是杀老怪 public void killBoss() { System.out.println(this.name + "在打怪!"); } //进游戏之前你肯定要登录吧,这是一个必要条件 public void login(String user, String password) { System.out.println("登录名为"+user + " 的用户 " + this.name + "登录成功!"); } //升级,升级有很多方法,花钱买是一种,做任务也是一种 public void upgrade() { System.out.println(this.name + " 又升了一级!"); } } //GamePlayerProxy.java public class GamePlayerProxy implements IGamePlayer { private IGamePlayer gamePlayer = null; //通过构造函数传递要对谁进行代练 public GamePlayerProxy(IGamePlayer _gamePlayer){ this.gamePlayer = _gamePlayer; } //代练杀怪 public void killBoss() { this.gamePlayer.killBoss(); } //代练登录 public void login(String user, String password) { this.gamePlayer.login(user, password); } //代练升级 public void upgrade() { this.gamePlayer.upgrade(); } } //Client.java public class Client { public static void main(String[] args) { //定义一个痴迷的玩家 IGamePlayer player = new GamePlayer("张三"); //然后再定义一个代练者 IGamePlayer proxy = new GamePlayerProxy(player); //开始打游戏,记下时间戳 System.out.println("开始时间是:2009-8-25 10:45"); proxy.login("zhangSan", "password"); //开始杀怪 proxy.killBoss(); //升级 proxy.upgrade(); //记录结束游戏时间 System.out.println("结束时间是:2009-8-26 03:40"); } }
代理模式的扩展
几种常用的代理模式
图片代理:一个很常见的代理模式的应用实例就是对大图浏览的控制。用户通过浏览器访问网页时先不加载真实的大图,而是通过代理对象的方法来进行处理,在代理对象的方法中,先使用一个线程向客户端浏览器加载一个小 图片,然后在后台使用另一个线程来调用大图片的加载方法将大图片加载到客户端。当需要浏览大图片时,再将大图片在新网页中显示。如果用户在浏览大图时加载 工作还没有完成,可以再启动一个线程来显示相应的提示信息。通过代理技术结合多线程编程将真实图片的加载放到后台来操作,不影响前台图片的浏览。
远程代理:远程代理可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的远程业务对象是局域的而不是远程的,而远程代理对象承担了大部分的网络通信工作。
虚拟代理:当一个对象的加载十分耗费资源的时候,虚拟代理的优势就非常明显地体现出来了。虚拟代理模式是一种内存节省技术,那些占用大量内存或处理复杂的对象将推迟到使用它的时候才创建。
在应用程序启动的时候,可以用代理对象代替真实对象初始化,节省了内存的占用,并大大加速了系统的启动时间。
动态代理
动态代理是一种较为高级的代理模式,它的典型应用就是Spring AOP。在传统的代理模式中,客户端通过Proxy调用RealSubject类的request()方法,同时还在代理类中封装了其他方法(如preRequest()和postRequest()),可以处理一些其他问题。如果按照这种方法使用代理模式,那么真实主题角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实主题角色必须对应一个代 理主题角色,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数,此外,如何在事先不知道真实主题角色的情况下使用代理主题角色,这都是 动态代理需要解决的问题。
笔者:扩展这块如果感兴趣,可以看看《设计模式之禅》第十二章,P119-P134,讲的灰常详细
代理模式优缺点
优点
1.0
代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
远程代理使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
保护代理可以控制对真实对象的使用权限。
2.0
职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职务的事务,通过后期的代理完成一件事务,附带的结果就是编程的简洁清晰。
高扩展性。具体主题角色是随时都会发生变化的,只要他实现了接口,不管他如何变化,他都逃不脱接口,那我们的代理类完全就可以在不做任何修改的情况下使用。
智能化。这个在动态代理的章节就会看到代理的智能化。
缺点
由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理模式适用环境
根据代理模式的使用目的,常见的代理模式有以下几种类型:
远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地 的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在 另一台主机中,远程代理又叫做大使(Ambassador)。
虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟 到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个 开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
防火墙(Firewall)代理:保护目标不让恶意用户接近。
同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。
代理模式应用
EJB、Web Service等分布式技术都是代理模式的应用。在EJB中使用了RMI机制,远程服务器中的企业级Bean在本地有一个桩代理,客户端通过桩来调用远程 对象中定义的方法,而无须直接与远程对象交互。在EJB的使用中需要提供一个公共的接口,客户端针对该接口进行编程,无须知道桩以及远程EJB的实现细 节。
代理模式总结
在代理模式中,要求给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做Proxy或Surrogate,它是一种对象结构型模式。
代理模式包含三个角色:抽象主题角色声明了真实主题和代理主题的共同接口;代理主题角色内部包含对真实主题的引用,从而可以在任何时候操作真实主题对象; 真实主题角色定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的方法。
代理模式的优点在于能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;其缺点在于由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,并且实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
远程代理为一个位于不同的地址空间的对象提供一个本地的代表对象,它使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建,这个小对象称为虚拟代理。虚拟代理通过使 用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
保护代理可以控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
参考
-
《设计模式之禅》
-
课本
-
http://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/proxy.html
-
博客园 http://www.cnblogs.com/BeyondAnyTime/archive/2012/07/04/2576865.html
-
博客园2 http://www.cnblogs.com/java-my-life/archive/2012/04/23/2466712.html
---
本文章采用 知识共享署名2.5中国大陆许可协议 进行许可,转载必须注明作者和本文链接。
---
发表评论