为另一个对象提供一个替身或占位符以访问这个对象。
当你需要从网络上面查看一张很大的图片时,你可以使用代理模式先查看它的缩略图看是否是自己想要的图片。
代理模式为另一个对象提供代表,以便控制客户对对象的访问。
比如用户需要查看某些重要文件时,你可以使用代理模式首先检查这个用户是否有这个权限。
因为用户持有的是代理对象的引用并不是执行用户请求的对象,所以它实现了调用者与被调用者的完全解耦!
UML :
示意图:
为什么代理对象和真正的对象要实现同一个接口?
实现了同一个接口就可以保证只要能够使用真正做事对象的地方就能够用代理对象代替!
保存一个引用使得代理可以访问实体;
实现与 Subject 接口,这样代理就能在使用真正对象的地方使用代理对象;
控制对象的存取,并可能负责创建和删除它。
定义 RealSubject 和 Proxy 的共同接口,这样就可以在任何使用 RealSubject 的地方都可以使用 Proxy 。
定义被 Proxy 代理的对象。
现在小王同学要买一张回家的火车票,于是他来到了火车票代售点来买票。到了代售点他告诉阿姨他要买回家的火车票,阿姨顺利的帮小王买到了回家的车票。
现在我们来分析一下阿姨帮小王买到车票的过程:实际上代售点是不卖票的,真正卖票的是火车站。
流程示意图:
1 package com.tony.proxy; 2 3 /** 4 * Subject:Proxy和RealSubject必须实现此接口 5 * 提供售票操作 6 */ 7 public interface Subject { 8 void soldTickets(); 9 }
1 package com.tony.proxy; 2 /** 3 * 4 * 代售点(Proxy):持有真正做事对象的引用 5 */ 6 public class Proxy implements Subject { 7 8 private Subject realSubject; 9 10 public Proxy(){ 11 realSubject = new RealSubject(); 12 } 13 14 @Override 15 public void soldTickets() { 16 System.out.println("代售点:有顾客需要买火车票..."); 17 realSubject.soldTickets(); 18 System.out.println("代售点:正在出票..."); 19 } 20 21 }
1 package com.tony.proxy; 2 3 /** 4 * 5 * 火车站(RealSubject):真正处理请求的对象 6 */ 7 public class RealSubject implements Subject { 8 9 @Override 10 public void soldTickets() { 11 System.out.println("火车站:已接收到订单,允许出票..."); 12 } 13 14 }
1 package com.tony.proxy; 2 3 public class Client { 4 5 private Subject proxy; 6 7 public Client(){ 8 proxy = new Proxy(); 9 } 10 11 public void buyTickets(){ 12 System.out.println("小王:我要买票..."); 13 proxy.soldTickets(); 14 System.out.println("小王:已买到票..."); 15 } 16 17 }
1 package com.tony.proxy; 2 3 public class Test { 4 public static void main(String[] args) { 5 Client c = new Client(); 6 c.buyTickets(); 7 } 8 }
因为火车站的售票系统很重要,不能让所有人都能对它直接进行操作,所以火车站弄一个安全的代理(代售点)。想买票,没问题,去我的代理那里买我不直接对你们售票。这就在一定程度上保障了售票系统的安全。这也是使用代理模式的一个好处:对访问进行控制,只对有权限的对象开放。
大家可能会发现两种模式的UML图几乎一致,是不是意味着这两种模式可以相互代替呢?
不可以!两种模式的目的不一样:
装饰者的目的是为对象增加新的功能,而代理则是控制对某个对象的访问!
代理模式和适配器模式都是挡在其他对象的前面,并负责将请求转发给它们。
但是它们在结构上不一样:适配器会改变对象适配的接口,而代理则实现相同的接口。
(1)在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢,影响系统性能。
(2)增加了系统的复杂度。 就和其他的包装者( wrapper )一样,代理会造成你的设计中类的数目增加。
代理模式虽然会影响性能,但是它又会给我们带来很多好处:它可以对某个对象进行访问控制,提高系统的安全性;它可以是调用者和被调用者实现完全的解耦,提高了系统的弹性。在程序设计的时候我们必须要在系统性能和程序的安全性,可扩展性等进行权衡:想要设计出好的程序,牺牲一些性能也是值得的!
代理模式的应用非常广泛,几乎所有优秀的开源框架都用了:Spring、myBatis、Struts2等等。
所以要想设计出优秀的程序必须要掌握代理模式。
代理模式有许多变体,例如:缓存代理、同步代理、防火墙代理和写入时复制代理、智能引用代理、复杂隐藏代理。
代理模式的一个分支 —— 动态代理在我们平时编程经常用到,因为它给了我们程序的极大的灵活性。下一篇文章我将会对动态代理进行详细的解释。
《Head First 设计模式》
《设计模式》