代理模式(Proxy)在现实中,一个子系统或者一个组件不能或不应该被应用程序直接访问。例如,不是所有应用程序都有权使用某个组件(如某数据库)的服务,或检索一个组件提供的特殊信息。这就需要对服务和组件的访问进行保护和控制。代理设计模式是一种对某些组件进行访问控制和保护的模式。代理是一种在应用程序和访问对象之间建立的桥梁或关卡。通过代理的虚拟作用,达到控制对象访问的目的。使用代理往往有几种情况:1)被访问对象的资源由于地域、运行开销等因素的限制,不能获得对它的访问操作。例如,在远程网络中的资源,由于其规模或安全的限制,不能直接下载到本地计算机工作。只能通过一个本地的代理对象来实现对它的操作。几乎所有的数据库服务器,COM/DCOM组件或CORBA组件等都是通过本地代理实现对其资源的访问的。这就如同大公司的代理公司。2)被访问的对象结构比较复杂或多变时,例如,对异构数据资源的访问。由于应用端不可能把握资源的细节情况,只能由代理根据应用请求的性质,智能性地确定访问操作的执行。此时,代理起到一种分析、决策、组织和协调的作用,为应用程序提供了统一的服务接口。而访问操作的复杂性由代理解决。此时代理如同生活中的咨询和服务公司。3)出于某种安全的考虑,需要隐藏被访问对象的标识时,这时就需要一个公开的代理,并在它的掩护下完成对实际对象的访问操作。Internet代理服务商就属于这种代理模式。由上可知,代理具有的特性和所起到的作用可以包:间接性、委托性、虚拟性、安全性、保护性、远程性和多源性。问题与方案一个客户程序(应用程序)需要访问一个组件的服务。这种组件泛指普通的对象,外部数据库或者万维网上的一个网页等。直接访问在技术上是可行的,但通常不是最好的途径。直接访问组件在许多情况下是不合适的。如:一般不想把组件的物理地址强制编码到客户程序中。又如公司工程人员经常查询数据库以得到必要的资料、数据或蓝图,每次远程访问可能代价昂贵,而许多访问都是相似的或重复的。对远程组件的直接和无限制的访问常常是低效的和不安全的。因此需要对访问进行控制,从而引入了代理。代理设计模式是使客户程序与组件代表通信而不是与组件本身直接通信,这种代表称为代理(proxy)。它提供了组件接口并执行附加的前期和后期处理,例如访问控制检查,制作原件的只读副本等。引入代理带来许多好处,如提高效率、易于存取和防止越权访问等。引入代理结构时,应考虑下列要求:1)对组件的访问应该是高效的,花费适当的,对客户程序和组件双方都是安全的。2)客户程序通过代理访问组件应该是透明的、简单的、客户程序不必改变原先访问其他可直接访问组件的呼叫方法和语法。3)客户程序应了解远程服务访问的性能和经济方面的代价。结构与行为在代理的结构模式中,被客户应用程序访问的不再是孤立一个对象。而是具有特定关系的两个对象。一个充当另一个的代理。但是,对于客户程序来讲,这种代理关系是透明的。为此,代理模式的结构需要建立代理和被代理对象(称为原件)的特别连接关系。图1给出了代理模式的结构。在图1中,客户程序需要访问ActualObject类的对象和它的服务方法Service()。但是这种访问需要通过其代理Proxy类对象。因此,ActualObject类和Proxy类的应该具有相同的访问接口。如果它们都从公共时Object类导出就可以满足这个要求。但是Proxy类和ActualObject类的具体实现是完全不同的。要做到这一点,就需要Proxy类的对象把对它的接口的访问,转移到问ActualObject类的对象中去具体实现。然后把执行的结果返回给客户程序。可见,除了具有公共接口外,两个类的内部结构和算法的实现是完全不同的。在图1中Proxy类数据成员包含的对ActualObject类的对象的引用就起到了转移操作请求的作用。下面简述代理结构典型的动态行为:1)客户程序在执行他的任务中,要求代理结构实施一种服务。2)代理接受新到来的服务请求,并对它进行前期处理。前期处理包括查找要求的原件地址或检查本地存储器,查看被请求的信息是否己在存储器中等。3)若代理发现必须请求原件实现服务,则代理采用合适的通信协议和安全措施将该请求转发到原件。4)原件接受代理发来的请求并实现它,然后原件将响应返送回代理。5)代理接受响应,并在将响应送到客户程序之前(或后)实施附加的后期处理,如存储结果,撤销原件或释放一个资源锁。设计实现1)为处理对一个组件(原件)的访问控制,为它确定应有的控制职责,并将这些职责放到一个独立的代理组件上。2)引入一个抽象基类来详细说明代理和原件两种接口的共同部分,进而从这个抽象基类导出代理和原件。如果两者具有相同接口不可行,那么可加入一个适配器来保证接口的适应性。3)实现代理的功能,检查第1步中规定的控制职责。4)解除原件及客户程序中已迁移到代理中的职责。5)通过给代理一个指向原件的句柄将代理与原件连结。这个句柄可以是一个指针,一个引用,一个地址,一个标识符,一个套接字或一个端口等。6)删除原件及客户程序之间所有的直接关联,用代理的类似关系取代它们。在上面的实现中,最关键的是建立代理和原件之间的连接关系。一般的引用关系可以解决代理连接的多方面的需求。即使是外存对象的代理,其实现也是较直接和简单的。但是对于远程的和多源的连接,实现结构要复杂些。图(a)(b)表示了这两种连接的关系。图(a)表示了网络的代理连接。如同在网络中运行的数据库服务器一样,为了建立代理和服务器的连结,需要在客户程序端安装特别的服务程序(代理),使其配置指向服务器的网络地址。在服务器端应建立网络监听程序,负责接受和解释服务请求,并返回响应。图(b)表示多源性的连接。在代理的协调和选择下,由多个对象完成应用请求的情况、此时,代理的请求处理过程往往是一个问题求解的过程,需要内容广泛,形式各异的信息处理。智能体Agent的研究就属于这一类代理对象。另一个多源性代理的情况发生在异构和异地数据库信息的访问上。客户程序提出对某种信息的查询请求。这种信息可能以各种不同的结构形式存储在不同的数据库中,代理知道如何去得到这些信息,但往往需要多方面地搜索和计算。代理的各种变异形式代理可以应用于不同的目的,不同的形式中,这里有几种常用的代理。(1)远程代理。远程访问中的客户程序应该与网络地址和进程间通信协议相屏蔽,因此远程代理在客户端封装和保持了远程对象(原件)的物理位置。代理实现与远程对象间实际通信的进程间通信(IPC)程序。通常可用远程对象所需的地址空间来实例化代理。对于较复杂的IPC机制,可将与远程对象通信的职责转移到转发器组件来细化代理。(3)保护代理。在需要防止越权访问组件的情况下,保护代理保护原件(被访问组件对象)免受越权访问。保护代理需要检查每个客户程序的访问权限。这时可以使用平台所提供的访问控制机构,也可以给出客户程序到其他组件的许可集。另外,访问控制列表也是保护代理常用的实现手段。(3)缓存代理。为了多个本地客户程序可共享远程访问的返回结果,应该使用缓存代理。这时可用一个临时保存结果的数据区-缓存来扩充代理,并要求开发一种策略来保持和更新缓存。当缓存存满而又需要释放空间给新输入时,可采取不同策略。可以删除使用最少的缓存内容,或实现前移策略,当客户程序访问某缓存内容时,此部分内容被移到链表前端,而链表尾部的内容将被新的输入覆盖。在缓存代理中还应注意缓冲无效问题,当原件的数据变动时,在别处存储的数据副本将变得无效。此时,可采取不同的策略。对时效性很强数据,可采取直写策略。一旦原件改变,其所有的副本同时被改写。若副本很多或副本在远程,则可采用当输入的原体数据变化时,宣布整个缓冲无效。若客户程序可接受少量过期信息,则可用截止日期来标注每个缓存输入的有效期。(4)同步代理。同步代理可控制多个客户程序对某组件的同时访问。同步代理每次只允许一个或几个客户程序访问原件。代理可以用信号灯实现排斥处理或采用操作系统提供的任何同步方法,还可以区分读和写的操作访问。(5)计数代理。计数代理可用来收集组件使用的统计数字,也可以用来自动删除过时的对象。后者使用引用计数技术。这种计数代理保存对原件的引用计数,当引用计数变为零时就删除原件。(6)虚拟代理。当一个客户程序的服务请求到达,但代理中存在的信息不足时,虚拟代理负责将硬盘中所需的数据装入内存,并将请求转发到新创建或扩充的组件。如果原来组件已完全装入,则代理仅转发请求。转发请求的操作完全是透明的。不管原组件是否已全部装入内存,客户程序使用的接口是完全相同的。当不再需要此组件或其中一部分时,客户程序会通知代理。代理随后释放分配的空间。(7)防火墙代理。防火墙代理用来将本地客户机与外部网络相隔离。防火墙代理包含与具有潜在危险的外部环境进行通信所必须的连网技术和保护代码。在有防火墙的机器上,防火墙作为一个守护进程。该机器称为代理服务器。所有将请求传递到外部网络的客户程序均需要引用此代理。该代理在幕后工作。它检查输出的请求及返回的应答,以保证内部安全和访问规则。如果某请求不遵守这些规则或资源耗尽时,它就拒绝访问。正常时,客户程序访问外部并不感到任何阻碍。但防火墙却使内部用户不受外部的攻击,因为防火墙背后的内部网络的结构是隐藏的。因为所有的通信流都是通过防火墙代理,所以它构成一个潜在的瓶颈。但是他也为最优化(如设置缓存)提供一个理想的场所。同时,他也为附加任务如登录和核算提供一个理想位置。代理模式的应用及优缺点代理结构应用在许多地方,最突出的例子有:(1)万维网代理----通常运行在有防火墙机器上的HTTP服务器上。这种代理允许防火墙内部的人并发地访问外部网络,通过缓冲存储最新文件来提高效率。(2)动态嵌入与链接----OLE服务器可以作为动态链接到客户机地址空间的库来实现,也可以作为独立进程来实现。代理负责对客户隐瞒一个特殊服务是本地还是远程的信息。如果客户调用的是本地地址空间的服务时,它将直接调用此服务实现。如果是远程服务则代理负责取参数,打包并产生一个远程调用到远程服务器,然后当结果信息返回时,代理负责解包并把结果返回给客户。代理结构有如下优点:1)将服务器组件的位置与客户程序相分离。将所有本地信息和定位功能放入远程代理中,则客户程序不会因服务器或连网基础结构的变动而受影响。这使客户程序的代码变得更稳定和利于重用。2)将内务处理代码从功能中分离出来。一个代理减轻了客户程序的负担,使这些内务处理代码由代理来承担。3)提高效率,降低成本。虚拟代理帮助实现了“按需装入”的原则。它避免了不必要的磁盘导入,从而加速了应用程序的执行。缓冲代理提高了访问的效率,降低了成本。代理结构的不足有:(l)间接方式导致了低效率。所有的代理都引入了一个间接的附加层。由于代理造成的效率损失和客户程序的清晰度以及通过缓存增加的效率相比通常是可以忽略的。但有时需要进行两者的权衡。(2)过分采用复杂策略。当被访问的组件是高度动态时,例如预订飞机票系统,由于被访问原件数据的频繁变化,需要引入缓冲失效等处理策略,这往往使得策略复杂而使预定的目的失效。通常只有粗粒度的实体才能证实缓冲维护是正确的。