1.概述
因為某個對象消耗太多資源,而且你的代碼并不是每個邏輯路徑都需要此對象, 你曾有過延遲建立對象的想法嗎 ( if和else就是不同的兩條邏輯路徑) ? 你有想過限制通路某個對象,也就是說,提供一組方法給普通使用者,特别方法給管理者使用者?以上兩種需求都非常類似,并且都需要解決一個更大的問題:你如何提供一緻的接口給某個對象讓它可以改變其内部功能,或者是從來不存在的功能? 可以通過引入一個新的對象,來實作對真實對象的操作或者将新的對象作為真實對象的一個替身。即代理對象。它可以在用戶端和目标對象之間起到中介的作用,并且可以通過代理對象去掉客戶不能看到的内容和服務或者添加客戶需要的額外服務。 例子1:經典例子就是網絡代理,你想通路facebook或者twitter ,如何繞過gfw,找個代理網站。 例子2:可以調用遠端代理處理一些操作如圖:
</blockquote>
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代理:它是虛拟代理的一種,把複制(克隆)操作延遲到隻有在用戶端真正需要時才執行。一般來說,對象的深克隆是一個開銷較大的操作,copy-on-write代理可以讓這個操作延遲,隻有對象被用到的時候才被克隆。
5. 結構
uml圖:
簡單結構示意圖:
6.模式的組成
1)代理角色(proxy): . 儲存一個引用使得代理可以通路實體。若 realsubject和subject的接口相同,proxy會引用subject。 . 提供一個與subject的接口相同的接口,這樣代理就可以用來替代實體。 . 控制對實體的存取,并可能負責建立和删除它。 . 其他功能依賴于代理的類型: • remote proxy負責對請求及其參數進行編碼,并向不同位址空間中的實體發送已編碼的請求。 • virtual proxy可以緩存實體的附加資訊,以便延遲對它的通路。 • protection proxy檢查調用者是否具有實作一個請求所必需的通路權限。 2) 抽象主題角色(subject):定義真實主題角色realsubject 和 抽象主題角色proxy的共用接口,這樣就在任何使用realsubject的地方都可以使 用proxy。代理主題通過持有真實主題realsubject的引用,不但可以控制真實主題realsubject的建立或删除,可以在真實主題realsubject被調用前進行攔截,或在調用後進行某些操作. 3) 真實主題角色(realsubject):定義了代理角色(proxy)所代表的具體對象.
7. 效果
proxy模式在通路對象時引入了一定程度的間接性。根據代理的類型,附加的間接性有多種用途: 1) remote proxy可以隐藏一個對象存在于不同位址空間的事實。也使得用戶端可以通路在遠端機器上的對象,遠端機器可能具有更好的計算性能與處理速度,可以快速響應并處理用戶端請求。 2) virtual proxy 可以進行最優化,例如根據要求建立對象。即通過使用一個小對象來代表一個大對象,可以減少系統資源的消耗。 3) protection proxies和smart reference都允許在通路一個對象時有一些附加的内務處理(housekeeping task) 。 proxy模式還可以對使用者隐藏另一種稱之為寫時複制(copy-on-write)的優化方式,該優化與根據需要建立對象有關。拷貝一個龐大而複雜的對象是一種開銷很大的操作,如果這個拷貝根本沒有被修改,那麼這些開銷就沒有必要。用代理延遲這一拷貝過程,我們可以保證隻有當這個對象被修改的時候才對它進行拷貝。在實作copy-on-write時必須對實體進行引用計數。拷貝代理僅會增加引用計數。隻有當使用者請求一個修改該實體的操作時,代理才會真正的拷貝它。在這種情況下,代理還必須減 少實體的引用計數。當引用的數目為零時,這個實體将被删除。copy-on-write可以大幅度的降低拷貝龐大實體時的開銷。 代理模式能夠協調調用者和被調用者,在一定程度上降低了系統的耦合度。 代理模式的缺點 由于在用戶端和真實主題之間增加了代理對象,是以有些類型的代理模式可能會造成請求的處理速度變慢。 實作代理模式需要額外的工作,有些代理模式的實作非常複雜。
8.實作
我們用擷取天氣預報的例子說明代理模式: [php] view plain copy <?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; $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()); header('content-type:text/html;charset=utf-8'); client::main(); //client::genericmain();
9. 與其他相關模式
1)擴充卡模式adapter:擴充卡adapter 為它所适配的對象提供了一個不同的接口。相反,代理提供了與它的實體相同的接口。然而,用于通路保護的代理可能會拒絕執行實體會執行的操作,是以,它的接口實際上可能隻是實體接口的一個子集。 2) 裝飾器模式decorator:盡管decorator的實作部分與代理相似,但 decorator的目的不一樣。decorator為對象添加一個或多個功能,而代理則控制對對象的通路。
10.總結
代理模式在很多情況下都非常有用,特别是你想強行控制一個對象的時候,比如:延遲加載,監視狀态變更的方法等等
1、“增加一層間接層”是軟體系統中對許多負責問題的一種常見解決方法。在面向對象系統中,直接使用某些對象會帶來很多問題,作為間接層的proxy對象便是解決這一問題的常用手段。 2、具體proxy設計模式的實作方法、實作粒度都相差很大,有些可能對單個對象作細粒度的控制,有些可能對元件子產品提供抽象代理層,在架構層次對對象作proxy。
3、proxy并不一定要求保持接口的一緻性,隻要能夠實作間接控制,有時候損及一些透明性是可以接受的。例如上面的那個例子,代理類型proxyclass和被代理類型longdistanceclass可以不用繼承自同一個接口,正像gof《設計模式》中說的:為其他對象提供一種代理以控制這個對象的通路。代理類型從某種角度上講也可以起到控制被代理類型的通路的作用。