1.概述
由于某个对象消耗太多资源,而且你的代码并非每一个逻辑路径都须要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ?
你有想过限制訪问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户?
以上两种需求都很类似,而且都须要解决一个更大的问题:你怎样提供一致的接口给某个对象让它能够改变其内部功能,或者是从来不存在的功能? 能够通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象的一个替身。即代理对象。它能够,而且能够。
样例1:经典样例就是网络代理,你想訪问facebook或者twitter 。怎样绕过GFW。找个代理站点。
样例2:能够调用远程代理处理一些操作如图:
2.问题:
你如何才干在不直接操作对象的情况下,对此对象进行訪问?
3.解决方式
为其它对象提供一种代理。并以控制对这个对象的訪问。(Provide asurrogate or placeholderforanother object tocontrol access to it. )而对一个对象进行訪问控制的一个原因是为了仅仅有在我们确实须要这个对象时才对它进行创建和初始化。它是给某一个对象提供一个替代者(占位者),使之在client对象和subject对象之间编码更有效率。代理能够提供延迟实例化(lazy instantiation),控制訪问, 等等。包含仅仅在调用中传递。 一个处理纯本地资源的代理有时被称作虚拟代理。远程服务的代理经常称为远程代理。强制 控制訪问的代理称为保护代理。
4.有用性
在须要用比較通用和复杂的对象指针取代简单的指针的时候,使用 Proxy模式。
以下是一些能够使用Proxy模式常见情况:
1) 远程代理(Remote Proxy)为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间能够是在同一台主机中,也但是在还有一台主机中,远程代理又叫做大使(Ambassador)
2) 虚拟代理(Virtual Proxy)依据须要创建开销非常大的对象。假设须要创建一个资源消耗较大的对象。先创建一个消耗相对较小的对象来表示。真实对象仅仅在须要时才会被真正创建。3) 保护代理(Protection Proxy)控制对原始对象的訪问。保护代理用于对象应该有不同的訪问权限的时候。4) 智能指引(Smart Reference)代替了简单的指针。它在訪问对象时运行一些附加操作。 5) Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到仅仅有在client真正须要时才运行。一般来说。对象的深克隆是一个开销较大的操作,Copy-on-Write代理能够让这个操作延迟,仅仅有对象被用到的时候才被克隆。
5. 结构
Uml图:
6.模式的组成
简单结构示意图:
. 保存一个引用使得代理能够訪问实体。若 RealSubject和Subject的接口同样,Proxy会引用Subject。
. 提供一个与Subject的接口同样的接口。这样代理就能够用来替代实体。 . 控制对实体的存取,并可能负责创建和删除它。 . 其它功能依赖于代理的类型: • Remote Proxy负责对请求及其參数进行编码,并向不同地址空间中的实体发送已编 码的请求。 • Virtual Proxy能够缓存实体的附加信息,以便延迟对它的訪问。 • Protection Proxy检查调用者是否具有实现一个请求所必需的訪问权限。 定义真实主题角色RealSubject 和 抽象主题角色Proxy的共用接口,这样就在不论什么使用RealSubject的地方都能够使 用Proxy。代理主题通过持有真实主题RealSubject的引用,不但能够控制真实主题RealSubject的创建或删除,能够在真实主题RealSubject被调用前进行拦截,或在调用后进行某些操作.
定义了代理角色(proxy)所代表的详细对象.
7. 效果
Proxy模式在訪问对象时引入了一定程度的间接性。
依据代理的类型,附加的间接性有多种用途:
1) Remote Proxy能够隐藏一个对象存在于不同地址空间的事实。也使得client能够訪问在远程机器上的对象。远程机器可能具有更好的计算性能与处理速度。能够高速响应并处理client请求。
2) Virtual Proxy 能够进行最优化,比如依据要求创建对象。即通过使用一个小对象来代表一个大对象,能够降低系统资源的消耗。
3) Protection Proxies和Smart Reference都同意在訪问一个对象时有一些附加的内务处理(Housekeeping task) 。Proxy模式还能够对用户隐藏还有一种称之为写时复制(copy-on-write)的优化方式,该优化与依据须要创建对象有关。拷贝一个庞大而复杂的对象是一种开销非常大的操作。假设这个拷贝根本没有被改动,那么这些开销就没有必要。用代理延迟这一拷贝过程,我们能够保证仅仅有当这个对象被改动的时候才对它进行拷贝。在实现copy-on-write时必须对实体进行引用计数。拷贝代理仅会添加引用计数。仅仅有当用户请求一个改动该实体的操作时,代理才会真正的拷贝它。
在这样的情况下。代理还必须减
少实体的引用计数。当引用的数目为零时。这个实体将被删除。copy-on-write能够大幅度的减少拷贝庞大实体时的开销。
代理模式可以 协调调用者和被调用者。在一定程度上减少了系统的耦合度。代理模式的缺点 因为在client和真实主题之间添加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 实现代理模式须要额外的工作,有些代理模式的实现很复杂。
8.实现
我们用获取天气预报的样例说明代理模式:
- <?
php
- /**
- * 代理模式
- *
- * 为其它对象提供一个代理以控制这个对象的訪问
- *
- */
- /**
- * 抽象主题角色(Subject):天气
- *
- */
- interface Weather
- {
- public function request($city);
- public function display($city);
- public function isValidCity($city);
- }
- /**
- * 真实主题角色(RealSubject):
- *
- */
- class RealWeather implements Weather
- {
- protected $_url = 'http://www.google.com/ig/api?
&oe=utf-8&hl=zh-cn&weather='
;- protected $_weatherXml = '' ;
- function __construct(){
- }
- public function request($city){
- $this->_weatherXml = file_get_contents($this->_url . $city );
- }
- public function display($city ){
- if ($this->_weatherXml == '') {
- $this->request($city);
- }
- //$this->_weatherXml = mb_convert_encoding($this->_weatherXml, 'UTF-8', 'GB2312');
- $weatherxml = simplexml_load_string($this->_weatherXml);
- $low = intval($weatherxml->weather->forecast_conditions[0]->low->attributes());
- $high = $weatherxml->weather->forecast_conditions[0]->high->attributes();
- $icon= 'http://www.google.com'. $weatherxml->weather->forecast_conditions[0]->icon->attributes();
- $condition=$weatherxml->weather->forecast_conditions[0]->condition->attributes();
- $weather = date('Y年n月j日').' 天气预报:<span class="cor_ff6c00 f_bold">'.$city_names[$city].' </span> <img class="v_middle" src="'.$icon.'" alt="'.$condition.'" width="16" height="17" align="absmiddle" /> <span class="f_bold"></span>: '.$low.'°C ~ '.$high.'°C '.$condition;
- echo $weather;
- }
- public function isValidCity($city){
- }
- }
- /**
- * 代理角色(Proxy):延迟代理
- *
- */
- class ProxyWeather implements Weather {
- private $_client ;
- private function client() {
- if (! $this->_client instanceof RealWeather) {
- $this->_client = new RealWeather();
- }
- return $this->_client;
- }
- public function request($city){
- $this->_client()->request($city);
- }
- public function isValidCity($city) {
- return $this->_client()->isValidCity($city);
- }
- public function display($city) {
- return $this->client()->display($city);
- }
- }
- /**
- * 代理角色(Proxy):动态代理
- *
- */
- class GenericProxyWeather {
- protected $_subject;
- public function __construct($subject) {
- $this->_subject = $subject;
- }
- public function __call($method, $args) {
- return call_user_func_array(
- array($this->_subject, $method),
- $args);
- }
- }
- class Client{
- static function main(){
- $proxy = new ProxyWeather();
- $report = $proxy->display('beijing');
- }
- static function Genericmain(){
- $proxy = new GenericProxyWeather(new RealWeather());
- $report = $proxy->display('beijing');
- }
- }
- header('Content-type:text/html;charset=UTF-8');
- Client::main();
- //Client::Genericmain();
9. 与其它相关模式
:适配器Adapter 为它所适配的对象提供了一个不同的接口。相反,代理提供了与它的实体同样的接口。然而,用于訪问保护的代理可能会拒绝运行实体会运行的操作,因此,它的接口实际上可能仅仅是实体接口的一个子集。
:虽然Decorator的实现部分与代理相似,但 Decorator的目的不一样。Decorator为对象加入一个或多个功能,而代理则控制对对象的訪问。
10.总结
代理模式在非常多情况下都非常实用,特别是你想强行控制一个对象的时候,比方:延迟载入,监视状态变更的方法等等
1、“添加一层间接层”是软件系统中对很多负责问题的一种常见解决方法。在面向对象系统中。直接使用某些对象会带来非常多问题。作为间接层的proxy对象便是解决这一问题的经常使用手段。
2、详细proxy设计模式的实现方法、实现粒度都相差非常大。有些可能对单个对象作细粒度的控制。有些可能对组件模块提供抽象代理层,在架构层次对对象作proxy。
3、proxy并不一定要求保持接口的一致性,仅仅要能够实现间接控制,有时候损及一些透明性是能够接受的。比如上面的那个样例,代理类型ProxyClass和被代理类型LongDistanceClass能够不用继承自同一个接口,正像GoF《设计模式》中说的:为其它对象提供一种代理以控制这个对象的訪问。代理类型从某种角度上讲也能够起到控制被代理类型的訪问的作用。