標籤:
設計模式(十一)代理模式Proxy(結構型)
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代理:它是虛擬代理的一種,把複製(複製)操作延遲到只有在用戶端真正需要時才執行。一般來說,對象的深複製是一個開銷較大的操作,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 print?
- <?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. 與其他相關模式
1)適配器模式Adapter:適配器Adapter 為它所適配的對象提供了一個不同的介面。相反,代理提供了與它的實體相同的介面。然而,用於訪問保護的代理可能會拒絕執行實體會執行的操作,因此,它的介面實際上可能只是實體介面的一個子集。
2) 裝飾器模式Decorator:儘管Decorator的實現部分與代理相似,但 Decorator的目的不一樣。Decorator為對象添加一個或多個功能,而代理則控制對對象的訪問。
10.總結
代理模式在很多情況下都非常有用,特別是你想強行控制一個對象的時候,比如:消極式載入,監視狀態變更的方法等等
1、“增加一層間接層”是軟體系統中對許多負責問題的一種常見解決方案。在物件導向系統中,直接使用某些對象會帶來很多問題,作為間接層的proxy對象便是解決這一問題的常用手段。
2、具體proxy設計模式的實現方法、實現粒度都相差很大,有些可能對單個對象作細粒度的控制,有些可能對組件模組提供抽象代理層,在架構層次對對象作proxy。
3、proxy並不一定要求保持介面的一致性,只要能夠實現間接控制,有時候損及一些透明性是可以接受的。例如
設計模式(十一)代理模式Proxy(結構型)