概述
在软件系统中,有些对象有时候由于跨越网络或者其他的障碍,而不能够或者不想直接访问另一个对象,如果直接访问会给系统带来不必要的复杂性,这时候可以在客户程序和目标对象之间增加一层中间层,让代理对象来代替目标对象打点一切。这就是本文要说的Proxy模式。
意图
代理(Proxy)模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理模式的英文叫做Proxy或Surrogate,中文都可译成"代理"。所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
<Design Pattern>Proxy模式结构图
图1 Proxy模式结构图
代理的种类
如果按照使用目的来划分,代理有以下几种:
远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。
虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。
Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
防火墙(Firewall)代理:保护目标,不让恶意用户接近。
同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
在所有种类的代理模式中,虚拟(Virtual)代理、远程(Remote)代理、智能引用代理(Smart Reference Proxy)和保护(Protect or Access)代理是最为常见的代理模式。
西游记中的例子
尽管那时候八戒还不叫八戒,但为了方便,这里仍然这样称呼他。
高老庄的故事
却说那春融时节,悟空牵着白马,与唐僧赶路西行。忽一日天色将晚,远远地望见一村人,这就是高老庄,猪八戒的丈人高太公家。为了将高家三小姐解救出八戒的魔掌,悟空决定扮做高小姐,会一会这个妖怪:
"行者却弄神通,摇身一变,变得就如那女子一般,独自个坐在房里等那妖精。不多时,一阵风来,真个是走石飞砂……那阵狂风过处,只见半空里来了一个妖精,果然生得丑陋:黑脸短毛,长喙大耳,穿一领青不青、蓝不蓝的梭布直裰,系一条花布手巾……走进房,一把搂住,就要亲嘴……"
高家三小姐的神貌和本人
悟空的下手之处是将高家三小姐的神貌和她本人分割开来,这和"开一闭"原则有异曲同工之妙。这样一来,"高家三小姐本人"也就变成了"高家三小姐神貌"的具体实现,而"高家三小姐神貌"则变成了抽象角色,如下图所示。
悟空扮演并代替高家三小姐
悟空巧妙地实现了"高家三小姐神貌",也就是说同样变成了"高家三小姐神貌"的子类。悟空可以扮演高家三小姐,并代替高家三小姐会见八戒,其静态结构图如下图所示。
悟空代替"高家三小姐本人"去会见猪八戒。显然这就是代理模式的应用。具体地讲,这是保护代理模式的应用。只有代理对象认为合适时,才会将客户端的请求传递给真实主题对象。
八戒分辨不出真假老婆
从《西游记》的描述可以看出,猪八戒根本份辨不出悟空扮演的"高家三小姐替身"和 "高家三小姐本人"。客户端分辨不出代理主题对象与真实主题对象,这是代理模式的一个
重要用意。
悟空代替高家三小姐会见八戒的对象图如下图所示。
示例用例图设计:
比如在大学时,每年学校都会举行元旦晚会,但学校是主办单位,而学生会是承办单位.这一模式正是我们的代理模式,用例图如下:
代码设计:
先创建接口IActivity.cs:
public interface IActivity { /// <summary> /// 主题 /// </summary> /// <returns></returns> string Subject(); /// <summary> /// 地点 /// </summary> /// <returns></returns> string Place(); /// <summary> /// 时间 /// </summary> /// <returns></returns> DateTime ActionDate(); }
然后创建UnderTakeActivity.cs:
public class UnderTakeActivity:IActivity { #region IActivity 成员 public string Subject() { return "元旦晚会!"; } public string Place() { return "逸夫馆"; } public DateTime ActionDate() { return DateTime.Now.AddDays(7); } #endregion }
再创建SchoolActivity.cs:
public class SchoolActivity:IActivity { private IActivity activity = new UnderTakeActivity(); #region IActivity 成员 public string Subject() { return activity.Subject(); } public string Place() { return activity.Place(); } public DateTime ActionDate() { return activity.ActionDate(); } #endregion }
最后调用:
public partial class Run : Form { public Run() { InitializeComponent(); } private void btnRun_Click(object sender, EventArgs e) { IActivity activity = new SchoolActivity(); rtbResult.AppendText(activity.Subject() + "\n"); rtbResult.AppendText(activity.Place() + "\n"); rtbResult.AppendText(activity.ActionDate().ToString()); } }
结果如下:
效果及实现要点
Proxy模式根据种类不同,效果也不尽相同:
1.远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部份的网络通讯工作。由于客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
2.虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载;代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的情况下,虚拟代理的好处就非常明显。
3.Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
4.保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。保护代理的好处是它可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
5.Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
6.防火墙(Firewall)代理:保护目标,不让恶意用户接近。
7.同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
8.智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
9.代理模式应用非常广泛,如果你希望降低对象的使用复杂度、或是提升对象使用的友好度、或是提高对象使用的效率都可以考虑代理模式。
总结
1.在软件系统中,增加一个中间层是我们解决问题的常见手法,这方面Proxy模式给了我们很好的实现。
2,Proxy、Facade以及Adapter可能都是对对象的一层封装,侧重点不同。Proxy基于一致的接口进行封装,Facade针对封装子系统,转化为高层接口,而Adapter的封装是处于适配接口的目的。