延遲代理
現(xiàn)在你基本掌握了PHP5風(fēng)格的SoapClient(如何做一個(gè)遠(yuǎn)程代理),但是你怎么才能寫一個(gè)延遲實(shí)例化的代理給SoapClient呢?
class GlobalWeather { private $client; // ‘Station getStation(string $code)’, public function getStation($code) { return $this->client->getStation($code); } }
getStation()可以代理$client變量指向的getStation()方法。不管如何,從這點(diǎn)上看, SoapClient實(shí)例并沒有創(chuàng)建,也沒有存儲(chǔ)到$client變量,因?yàn)樯厦嬉颜f過,對(duì)WSDL文件進(jìn)行遠(yuǎn)程處理應(yīng)該延遲到真正需要的時(shí)候。
你可以在插入一段延遲加載的代碼之前做一下client的調(diào)用,來延遲SoapClient的實(shí)例化
class GlobalWeather { private $client; private function lazyLoad() { if (! $this->client instanceof SoapClient) { $this->client = new SoapClient( ‘http://live.capescience.com/wsdl/GlobalWeather.wsdl’); } } // ‘Station getStation(string $code)’, public function getStation($code) { $this->lazyLoad(); return $this->client->getStation($code); } }
lazyLoad()中創(chuàng)建SoapClient對(duì)象是一定要的。這里存在一個(gè)問題:如果我是一個(gè)懶惰的編碼者,讓我非常不爽是:我不得不在所有的代理方法中加入$this->lazyLoad();。有更加簡(jiǎn)便的方法嗎?當(dāng)然有,重寫一遍吧,使用PHP5新的特性來返回對(duì)象。改lazyLoad()的名字為client(),并在這個(gè)方法里面實(shí)例化$client,代理中的方法訪問client()方法優(yōu)于訪問$client屬性。把延遲實(shí)例化做的更加簡(jiǎn)單!
class GlobalWeather { private function client() { if (! $this->client instanceof SoapClient) { $this->client = new SoapClient( ‘http://live.capescience.com/wsdl/GlobalWeather.wsdl’); } return $this->client; } // ... // ‘boolean isValidCode(string $code) public function isValidCode($code) { return $this->client()->isValidCode($code); } // and so on for other SOAP service methods ... // ‘WeatherReport getWeatherReport(string $code) The Proxy Pattern 199 public function getWeatherReport($code) { return $this->client()->getWeatherReport($code); } }
你迷上GlobalWeather服務(wù)的延遲實(shí)例代理類了嗎?你有一個(gè)類可以在任何時(shí)間在你的程序里面創(chuàng)建,并且在不需要它們的時(shí)候就不解析的遠(yuǎn)程資源。使用代理類還有另外一個(gè)優(yōu)勢(shì):使用代理可以列舉SOAP所支持的方法,你現(xiàn)在就可以對(duì)這個(gè)類進(jìn)行測(cè)試。
注:延遲代理可延遲異常 在PHP5里,創(chuàng)建一個(gè)對(duì)象會(huì)產(chǎn)生一個(gè)異常。使用延遲實(shí)例化代理,你可以延遲這個(gè)潛在的異常直到第一次使用方法創(chuàng)建對(duì)象的時(shí)候。。(你可以試試用代理完成這個(gè)功能。)這明顯不是代理模式的重點(diǎn),但是往往有一些事情,你需要記住。
動(dòng)態(tài)代理
PHP5提供一些很好的特性,可以快速的封裝一個(gè)代理類而不用明確的寫出每一個(gè)方法。
class GenericProxy { protected $subject; public function __construct($subject) { $this->subject = $subject; } public function __call($method, $args) { return call_user_func_array( array($this->subject, $method), $args); } }
這里的關(guān)鍵是_call()方法(通過EXPERIMENTAL擴(kuò)展重載,同樣可用于PHP4)。代理類中的_call方法允許你通過$subject代替的方式來重定義每個(gè)調(diào)用。由于__call()比其他方法的優(yōu)先級(jí)別都低,你可以在代理類中定義一個(gè)方法,讓__call()來代替執(zhí)行,于是你可以加一些特別的需求到你使用的代理模式。
總結(jié)
代理模式在很多情況下都非常有用,特別是你想強(qiáng)行控制一個(gè)對(duì)象的時(shí)候,比如:延遲加載,監(jiān)視狀態(tài)變更的方法等等。這章通過開發(fā)GlobalWeather類做示范,以后你也可以使用代理模式在你的本地計(jì)算機(jī)上使用遠(yuǎn)程資源:
點(diǎn)擊放大
動(dòng)態(tài)代理在編寫代碼的時(shí)候非常簡(jiǎn)單,因此可以很快速和容易的實(shí)現(xiàn)在你的程序中。然而(所有的實(shí)現(xiàn)都依賴__call()方法),反射并不能在對(duì)象內(nèi)部具備這樣的可見性(譯注:反射一般是在不了解類的內(nèi)部情況下操作的)。在特殊情況下,如果你使用一個(gè)代理就需要一個(gè)接口,你不能老是依賴于__call()方法,至少必須編碼的時(shí)候,應(yīng)該把接口所有的方法很明確的寫入你的代理類。
下文:《PHP設(shè)計(jì)模式介紹》第十二章 裝飾器模式
本文鏈接:http://www.95time.cn/tech/program/2008/6053.asp
出處:phpchina
責(zé)任編輯:bluehearts
上一頁(yè) php設(shè)計(jì)模式介紹之章代理模式 [3] 下一頁(yè)
◎進(jìn)入論壇網(wǎng)絡(luò)編程版塊參加討論
|