C#面向对象设计模式纵横谈:Proxy 代理模式
直接与间接
人们对于复杂的软件系统常常有一种处理手法,即增加一层间接层,从而对系统获得一种更为灵活、满足特定需求的解决方案。
假设A要访问B三次。如果A和B是分布式中的两个机器,那么A需要跨机器调用B三次就不是很好。如果在A和B之间加一个代理对象C,并且A和C处于同一个地址空间,即同一个机器。那么A和C之间通讯是非常高效的,现在A和C之间调用三次,到某个触发点的时候,和B只需要一次的通讯,这样性能就会好很多。这样做还有一个好处,即A不需要再知道分布式通讯的内容了。
现实生活中,其实操作系统就是软件和硬件之间的代理。
动机(Motivation)
在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦。如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方式。
意图(Intent)
为其他对象提供一种代理以控制对这个对象的访问。
——《设计模式》GoF
例说Proxy应用
HrSystem里面new的Employee对象将位于和HrSystem同样的地址空间里面,但如果我们需要把Employee作为跨互联网的调用,那么这样的代码就不适用了。改进后的代码:
Employee的代理应该和Employee具有同样的接口,它的实现很复杂。
其中,Employee类运行在Internet远端的一台机器上,而EmployeeProxy运行在本地的Windows Forms上。这里代理的目的是为了屏蔽分布式通讯、WebService的细节。
结构(Structure)
其中Subject就是HrSystem,它本来是要直接调用RealSubject的。但是由于WebSerivce这种情况,它需要间接的通过Proxy调用RealSubject。
Proxy模式的几个要点
“增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会来带很多问题,作为间接层的Proxy对象便是解决这一问题的常用手段。具体Proxy设计模式的实现方法、实现粒度都相差很大,有些可能对单个对象做细粒度的控制,如copy-on-write技术,有些可能对组件模块提供抽象代理层,在架构层次对对象做Proxy。
Proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。
.NET架构中的Proxy应用
WebService的一些例子:
代理对象MathService
客户端
客户端使用的是MathService类,这个类是在本地运行的。
另一个例子:Copy-on-Write
左边是堆,这样做是比较浪费内存的,因为系统中可能有很多字符串重复。目前大多数系统都是以下的做法:
但这样字符串就不能更改了,例如如果要把s1改为大写,那么必须要另起一个字符串变量:
C#当然也是允许对字符串更改的,不过不是string类型,而是StringBuilder类型。
这样sb就可以被改变。StringBuilder的原理:
sb、sb2和sb3都指向同一个字符串,如果sb把里面内容改变,那么就会把hello拷贝到另一块内存,再把内容进行更改。其实Copy-on-Write应该更准确地描述为Copy-on-Change。
StringBuilder其实就是一种代理,我们本意是想访问字符串的,StringBuilder就是一种可变字符串的代理,而且StringBuilder也没有和String保持接口的一致性。我们看看StringBuilder的源代码:
注意Replace方法,当需要改变字符串的内容时,步骤是先new一个新的String,然后在更改新String的内容。但如果只有一个StringBuilder,那么就不需要拷贝到新的区域,而是直接在原来的String上修改。