上一次说的是Singleton模式,讲了很长的篇幅。这次讲Proxy,有什么不对的地方欢迎大家指出。
Proxy,是一个什么样的东西呢?从字面上,我们很容易联想到Http的代理服务器。嗯,意思上面有点接近,大概就是通过某种方式为客户提供一个中间连接。比如说,一个远程客户端需要使用某个服务,也就是我们说的远程调用。假设这个服务在本地的时候是由class ServiceA提供的,那么很明显,它的某个属性很可能就是在访问本地的资源,如果在远程调用的时候也访问本地资源,那么就很可能会出错。怎么办呢?当然,我们可以专门写一个程序来进行访问,或者进行修正。但是这样做的话灵活性就非常低了,我们不得不为本地使用的和远程使用的分别写两个程序。
![](/images/cnblogs_com/sumtec/1580/t_tmp_can_delete_01.jpg)
![](/images/cnblogs_com/sumtec/1580/t_tmp_can_delete_02.jpg)
可以看见,上面无论哪一种方式都显得有点笨拙。因为你不得不写两个程序,因为使用了不同的类或者同一个类下面的不同成员,尽管大部分的逻辑是相同的。那么有没有更加灵活一点的办法呢?这就是要使用Proxy的其中一种情况了。我们可以这么做:
![](/images/cnblogs_com/sumtec/1580/t_tmp_can_delete_03.jpg)
首先我们按照本地的情况进行设计ServiceA,然后从ServiceA派生出RemoteProxy,在这个RemoteProxy里面进行远程调用情况的设计。比如说,可能为了安全起见,不允许远程设置资源,但可以获取,等等。其实Proxy本身没有什么很难理解的地方,只是在很多时候我们都忽略了这种设计模式。至于说Proxy模式的实现方法,并不一定是想我给出的结构那样,Proxy必须派生自ServiceA,可以是反过来的,也可以是Proxy和ServiceA有一个共同的Root,甚至可以通过接口的方式进行设计。其主要核心在于提供一个中间的连接层次,而不在于怎么提供这个中间的连接层次。用这个方法设计出来的程序,就很容易同时直接应用在本地调用和远程调用的模式里面了,只要在远程调用的时候使用
这种模式的应用领域并不仅仅限制于Remoting这样的应用场合,有时候在处理一些大量数据的时候也可以发挥一定的作用。比如说,你要设计一个Word处理软件,里面的字符、列、行、表格、图片、格式、字体等等,全部都是一个个的对象。因此,当你把文件读出来的时候,就需要构造一系列的对象,这些构造操作必然会占用不少的时间,尤其是一些耗用不少资源的大对象,比如大图片。然而我们分析一下就会发现,为什么非要一次过全部构造出来呢,基本上一个屏幕的大小有限,很多东西根本就不会一下子显示出来。我们也许可以在一开始的时候仅仅构造当前屏幕的对象,其他的对象可以放在某个队列里面等待系统空闲的时候构造。这么一来,用户就不会因为等待构造上百页的对象而不耐烦了,也许他只是想看看文章的摘要。但是说起来简单,要是我们真的把构造的顺序进行一定的调整,很可能跟文件的反序列化操作中的一个前提有所冲突:所有东西都是序列化的。也许你不得不为了把等待队列里面的某个东西构造出来,因为不知道这个东西在文件当中的什么位置,而重新把文件重头到尾访问一遍,尽管之前可能已经访问过一遍了。
这个时候Proxy模式就可以发挥作用了。我们完全可以这么做:
![](/images/cnblogs_com/sumtec/1580/t_tmp_can_delete_04.jpg)
其中大致的代码可能是这样的:
public class ProxyObject
{
public BaseObject Reload()
{
BaseObject bo;
ArrayList arr;
int index;
bo = ReloadObjectFromFile(fs, FilePosition);
arr = this.parent.children;
index = arr.IndexOf(this);
arr.RemoveAt(index);
arr.Insert(index, bo);
UpdateEverything();
return bo;
}
public int DetaillValue_A
{
get
{
return Reload().DetailValue_A;
}
}
}
上面的那个图没有给出继承关系,那是因为可以有不同的设计方法。而代码也并不是写得很好,主要是给出一个意思。有人说了,直接把这个功能做到具体的每一个SomeObject里面不可以吗?可以,但是实际的情况很可能会稍微复杂一点,为了生成这个对象,可能需要更多的额外的信息而不仅仅是FilePosition。如果我们直接把Reload的功能做到SomeObject里面的话,那么每一个Object就会要额外负担一些完全构造好之后不需要的东西。此外,利用Proxy这个模式,我们完全可以在Reload里面应用类似工厂方法或者原形等其他设计模式来减轻我们的编码负担。当然,你也可以反过来设计:
public class SomeObject
{
public int DetailValue_A
{
get
{
if (this.proxyObject != null)
{
proxyObject.Reload();
proxyObject = null;
}
return detaillValue_A;
}
}
}
当然,这么设计的话,Reload的内容可能就会有点改变。那么为什么需要ProxyObject这个类呢?很简单,我完全可以弄一个Queue用来保存所有的ProxyObject,然后系统在空闲的时候在背景线程里面一个一个的调用((ProxyObject) reloadQueue.Dequeue()).Reload(); 来完成所有东西的加载了。当然,也许你会想,我可以直接把Reload弄到BaseObject里面,然后对Queue的元素调用BaseObject.Reload()。这个和你的具体设计有关,这里把它抽出来作为一个ProxyObject,主要是为了体现这个设计模式。
Proxy模式还有什么应用价值?用来限制权限,其实这个在前面已经提到过了。这里再作一些详细的讲解。
有时候,我们的一些对象很可能对于不同的用户会有不同的访问权限。如果说权限和用户的分类都是相对固定的,比如只有一般用户和超级用户之分,那么就可以通过Proxy模式进行设计。简单点说,就是首先设计一个所有功能都实现了,并且都可以访问的类。然后根据不同的访问权限,设计一个代理,对于某些受限制的权限作出特殊处理。这样做有什么好处呢?至少在开发的时候不需要过多地考虑怎么拒绝访问,有什么东西有可能不可以被什么用户访问。反过来说,当你整个系统开发完了,你才发现漏考虑了某种情况,那么可以通过Proxy模式来打一个简易的“补丁”。用Proxy模式对一些已有功能进行限制,可能对于代码的修改量的要求会比较小。
我觉得Proxy模式对于我来说,其吸引力在于,可以和一些其他的模式联合起来达到更加好的效果。
文章来源:http://dotnet.blogger.cn/sumtec/articles/806.aspx