日韩天天综合网_野战两个奶头被亲到高潮_亚洲日韩欧美精品综合_av女人天堂污污污_视频一区**字幕无弹窗_国产亚洲欧美小视频_国内性爱精品在线免费视频_国产一级电影在线播放_日韩欧美内地福利_亚洲一二三不卡片区

《PHP設(shè)計(jì)模式介紹》第九章 觀(guān)測(cè)模式_PHP教程

編輯Tag賺U幣
教程Tag:暫無(wú)Tag,歡迎添加,賺取U幣!

推薦:《PHP設(shè)計(jì)模式介紹》第八章 迭代器模式
類(lèi)中的面向?qū)ο缶幊谭庋b應(yīng)用邏輯。類(lèi),就是實(shí)例化的對(duì)象,每個(gè)單獨(dú)的對(duì)象都有一個(gè)特定的身份和狀態(tài)。單獨(dú)的對(duì)象是一種組織代碼的有用方法,但通常你會(huì)處理一組對(duì)象或者集合。 屬性來(lái)自 SQL 查

一些面向?qū)ο蟮木幊谭绞�,提供了一種構(gòu)建對(duì)象間復(fù)雜網(wǎng)絡(luò)互連的能力。當(dāng)對(duì)象們連接在一起時(shí),它們就可以相互提供服務(wù)和信息。

通常來(lái)說(shuō),當(dāng)某個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),你仍然需要對(duì)象之間能互相通信。但是出于各種原因,你也許并不愿意因?yàn)榇a環(huán)境的改變而對(duì)代碼做大的修改。也許,你只想根據(jù)你的具體應(yīng)用環(huán)境而改進(jìn)通信代碼�;蛘�,你只想簡(jiǎn)單的重新構(gòu)造通信代碼來(lái)避免類(lèi)和類(lèi)之間的相互依賴(lài)與相互從屬。

問(wèn)題

當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),你如何通知其他對(duì)象?是否需要一個(gè)動(dòng)態(tài)方案――一個(gè)就像允許腳本的執(zhí)行一樣,允許自由連接的方案?

解決方案

觀(guān)測(cè)模式允許一個(gè)對(duì)象關(guān)注其他對(duì)象的狀態(tài),并且,觀(guān)測(cè)模式還為被觀(guān)測(cè)者提供了一種觀(guān)測(cè)結(jié)構(gòu),或者說(shuō)是一個(gè)主體和一個(gè)客體。主體,也就是被觀(guān)測(cè)者,可以用來(lái)聯(lián)系所有的觀(guān)測(cè)它的觀(guān)測(cè)者�?腕w,也就是觀(guān)測(cè)者,用來(lái)接受主體狀態(tài)的改變

觀(guān)測(cè)就是一個(gè)可被觀(guān)測(cè)的類(lèi)(也就是主題)與一個(gè)或多個(gè)觀(guān)測(cè)它的類(lèi)(也就是客體)的協(xié)作。不論什么時(shí)候,當(dāng)被觀(guān)測(cè)對(duì)象的狀態(tài)變化時(shí),所有注冊(cè)過(guò)的觀(guān)測(cè)者都會(huì)得到通知。

觀(guān)測(cè)模式將被觀(guān)測(cè)者(主體)從觀(guān)測(cè)者(客體)種分離出來(lái)。這樣,每個(gè)觀(guān)測(cè)者都可以根據(jù)主體的變化分別采取各自的操作。(觀(guān)測(cè)模式和Publish/Subscribe模式一樣,也是一種有效描述對(duì)象間相互作用的模式。)

觀(guān)測(cè)模式靈活而且功能強(qiáng)大。對(duì)于被觀(guān)測(cè)者來(lái)說(shuō),那些查詢(xún)哪些類(lèi)需要自己的狀態(tài)信息和每次使用那些狀態(tài)信息的額外資源開(kāi)銷(xiāo)已經(jīng)不存在了。另外,一個(gè)觀(guān)測(cè)者可以在任何合適的時(shí)候進(jìn)行注冊(cè)和取消注冊(cè)。你也可以定義多個(gè)具體的觀(guān)測(cè)類(lèi),以便在實(shí)際應(yīng)用中執(zhí)行不同的操作。

實(shí)例代碼

舉例來(lái)說(shuō),你可以使用觀(guān)測(cè)模式為你的PHP腳本來(lái)創(chuàng)建一個(gè)更靈活的記錄錯(cuò)誤的句柄。因?yàn)椋J(rèn)的錯(cuò)誤記錄句柄也許只會(huì)在屏幕上顯示一些出錯(cuò)信息,但是增強(qiáng)后的句柄還可以將出錯(cuò)信息寫(xiě)進(jìn)一個(gè)日志文件中,或?qū)⒊鲥e(cuò)信息寫(xiě)進(jìn)系統(tǒng)日志之中,或?qū)⒊鲥e(cuò)信息通過(guò)電子郵件發(fā)送出去,或利用聲音報(bào)告出錯(cuò)信息。你甚至還可以構(gòu)造一種有級(jí)別的報(bào)錯(cuò)方案,只允許向那些已經(jīng)為具體的出錯(cuò)信息注冊(cè)過(guò)的觀(guān)測(cè)者報(bào)告。從一般的警告信息到像數(shù)據(jù)庫(kù)失靈之類(lèi)的嚴(yán)重出錯(cuò)信息都可以報(bào)告。

下面,我們用觀(guān)測(cè)模式來(lái)為PHP創(chuàng)建一系列的類(lèi)來(lái)實(shí)現(xiàn)剛才所說(shuō)的那些功能。新建一個(gè)名為ErrorHandler的類(lèi),它就是觀(guān)測(cè)模式的主體,也就是被觀(guān)測(cè)者。再建另外兩個(gè)名為FileErrorLogger和EmailErrorLogger的類(lèi),它們是觀(guān)測(cè)客體(即觀(guān)測(cè)者)。FileErrorLogger類(lèi)將出錯(cuò)信息寫(xiě)入日志文件,EmailErrorLogger類(lèi)利用電子郵件發(fā)送出錯(cuò)信息。在UML中,可以表示如下:

為了實(shí)現(xiàn)以觀(guān)測(cè)模式為基礎(chǔ)的錯(cuò)誤記錄句柄,首先我們注意到作為觀(guān)測(cè)者的FileErrorLogger類(lèi)和EmailErrorLogger類(lèi)什么也不能做。那么,F(xiàn)ileErrorLogger類(lèi)是如何向一個(gè)文件寫(xiě)出錯(cuò)信息,EmailErrorLogger類(lèi)又如何發(fā)送電子郵件的?接下來(lái),讓我來(lái)看看用來(lái)實(shí)現(xiàn)觀(guān)測(cè)模式的技術(shù)細(xì)節(jié),然后,再集中精力來(lái)看看該模式的主體――ErrorHandler的細(xì)節(jié)。最后,再寫(xiě)一些錯(cuò)誤處理函數(shù)來(lái)調(diào)用這個(gè)ErrorHandler類(lèi)。

最后用下面的這一段代碼來(lái)表示:

//PHP4
$eh=&getErrorHandlerInstance();
$eh->attach(newEmailErrorLogger(
[email protected]));
$eh->attach(newFileErrorLogger(fopen(‘error.log’,’w’)));
set_error_handler(‘observer_error_handler’);
//...later
trigger_error(‘thisisanerror’);


ErrorHandler類(lèi)是一種單件模式(參考第4章:TheSingletonPattern)。它可以通過(guò)函數(shù)Attach()來(lái)注冊(cè)各種錯(cuò)誤信息觀(guān)測(cè)者,而set_error_handler()函數(shù)就是一個(gè)指向ErrorHandler類(lèi)的函數(shù)。最后,當(dāng)一個(gè)錯(cuò)誤信息被觸發(fā)后,所有的觀(guān)測(cè)者都會(huì)得到通知。

為了使這次觀(guān)測(cè)的操作生效,你的測(cè)試必須能證明所有的這些操作(將錯(cuò)誤信息寫(xiě)入日志,利用電子郵件發(fā)送錯(cuò)誤信息)都能得到執(zhí)行,并且能正常工作。簡(jiǎn)而言之,讓我們來(lái)看看一系列簡(jiǎn)單的測(cè)試。(和這個(gè)實(shí)例有關(guān)的其他更多實(shí)例,可以在本書(shū)附帶的源代碼中找到)

這里有FileErrorLogger類(lèi)聯(lián)合測(cè)試的一部分代碼:它用來(lái)測(cè)試當(dāng)FileErrorlogger類(lèi)被某個(gè)對(duì)象實(shí)例化時(shí),是否具有向一個(gè)文件寫(xiě)日志的能力。

classFileErrorLoggerTestCaseextendsUnitTestCase{
var$_fh;
var$_test_file=‘test.log’;
functionsetup(){
@unlink($this->_test_file);
$this->_fh=fopen($this->_test_file,‘w’);
}
functionTestRequiresFileHandleToInstantiate(){/*...*/}
functionTestWrite(){
$content=‘test’.rand(10,100);
$log=&newFileErrorLogger($this->_fh);
$log->write($content);
$file_contents=file_get_contents($this->_test_file);
$this->assertWantedPattern(‘/’.$content.’$/’,$file_contents);
}
functionTestWriteIsTimeStamped(){/*...*/}
}

在這個(gè)測(cè)試中,setup()函數(shù)創(chuàng)建了一個(gè)文件指針,指向一個(gè)名為“test.log”的新文件。并且,將該指針保存在變量$_fh中,這個(gè)可寫(xiě)的文件指針將作為一個(gè)變量傳遞給FileErrorlogger對(duì)象的實(shí)例,進(jìn)行測(cè)試。變量$content的值將傳遞給函數(shù)write(),并且,在存儲(chǔ)結(jié)束后,還將用來(lái)被檢查$content的值是否確實(shí)被正確寫(xiě)入test.log文件中。

(這個(gè)測(cè)試要求PHP必須具有向那個(gè)新建的test.log中寫(xiě)數(shù)據(jù)的權(quán)限。)

下面的一些代碼也許可以幫助FileErrorLogger類(lèi)通過(guò)測(cè)試。

classFileErrorLogger{
var$_fh;
functionFileErrorLogger($file_handle){
$this->_fh=$file_handle;
}
functionwrite($msg){
fwrite($this->_fh,date(‘Y-m-dH:i:s:‘).$msg);
}
}

一個(gè)類(lèi)似的測(cè)試代碼可以使EmailErrorLogger類(lèi)生效。

classEmailErrorLoggerTestCaseextendsUnitTestCase{
functionTestEmailAddressFirstConstructorParameter(){
$log=&newEmailErrorLogger;
$this->assertErrorPattern(‘/missing.*1/i’);
}
functionTestMail(){
$log=&newEmailErrorLogger(
[email protected]);
$log->mail(‘testmessage’);
}
}

接下來(lái),通過(guò)這個(gè)測(cè)試的EmailErrorLogger類(lèi)的代碼如下:

classEmailErrorLogger{
var$_addr;
var$_subject;
functionEmailErrorLogger($addr,
$subject=’ApplicationErrorMessage’){
$this->_addr=$addr;
$this->_subject=$subject;
}
functionmail($msg){
mail($this->_addr
,$this->_subject
,date(‘Y-m-dH:i:s:‘).$msg);
}
}

你是怎樣確定EmailErrorLogger類(lèi)能真正發(fā)送電子郵件的呢?是的,你可以打開(kāi)你的收件箱,看看其中是否有新郵件,就知道了。但是,那就不是一個(gè)全自動(dòng)的測(cè)試了�;蛘哒f(shuō),這個(gè)測(cè)試就只是偽模式的一個(gè)不錯(cuò)的替代方案。(至于如何創(chuàng)建一個(gè)控制郵件的類(lèi),將作為一個(gè)練習(xí)留給讀者的。詳細(xì)信息,請(qǐng)參考第6章TheMockObjectPattern或參考FakeMail項(xiàng)目http://sf.net/projects/fakemail/.)

有了合適而正確的觀(guān)測(cè)者,我們就可以在觀(guān)測(cè)模式下,從函數(shù)attach()開(kāi)始繼續(xù)測(cè)試ErrorHandler類(lèi)。

classObserver{
functionupdate(){
die(‘abstractmethod’);
}
}
Mock::Generate(‘Observer’);
classErrorHandlerTestCaseextendsUnitTestCase{
functionTestAttach(){
$eh=&newErrorHandler;
$observer=&newMockObserver($this);
$observer->expectOnce(
‘update’
,array(‘*’));//array(&$eh)
$eh->attach($observer);
$eh->notify();
$observer->tally();
}
functionTestDetach(){/*...*/}
}

在這次測(cè)試中,一個(gè)簡(jiǎn)單的觀(guān)測(cè)類(lèi)被創(chuàng)建出來(lái),作為所有觀(guān)測(cè)者的接口。為了測(cè)試函數(shù)attach(),一個(gè)基于這個(gè)觀(guān)測(cè)類(lèi)的偽模式被創(chuàng)建出來(lái),并且和ErrorHandler測(cè)試實(shí)例關(guān)聯(lián)在一起。然后,當(dāng)公共函數(shù)notify()被調(diào)用時(shí),偽模式將證實(shí)update()函數(shù)曾經(jīng)被調(diào)用過(guò)。

請(qǐng)注意剛才提及的的在模擬觀(guān)測(cè)中所創(chuàng)建的函數(shù)array(&$eh)中的參數(shù)。在理想狀態(tài)中,那個(gè)測(cè)試應(yīng)該可以通過(guò)的。然而,由于PHP語(yǔ)言的限制,這將產(chǎn)生一個(gè)致命錯(cuò)誤:“NestingLevelTooDeep――循環(huán)依賴(lài)?”。為了避免出現(xiàn)那樣的問(wèn)題,代碼中必須使用簡(jiǎn)單測(cè)試下“WildCard”功能,以便允許所有參數(shù)都能像預(yù)期的那樣傳遞。

NestingLevelTooDeep

因?yàn)镋rrorHandler在數(shù)組$_observer中包含涉及到模擬觀(guān)測(cè)的參數(shù),本來(lái)預(yù)期是要將它傳遞給模擬觀(guān)測(cè)的。所以,PHP產(chǎn)生一個(gè)“NestingLevelTooDeep”錯(cuò)誤。而循環(huán)依賴(lài)就像一個(gè)初級(jí)的PHP問(wèn)題,甚至可以在一個(gè)簡(jiǎn)單的PHP環(huán)境中發(fā)現(xiàn)它。(請(qǐng)參考http://bugs.php.net/bug.php?id=31449.)

分享:《PHP設(shè)計(jì)模式介紹》第七章 策略模式
在編寫(xiě)面向?qū)ο蟮拇a的時(shí),有些時(shí)候你需要一個(gè)能夠自己根據(jù)不同的條件來(lái)引入不同的操作對(duì)象實(shí)例。例如,一個(gè)菜單功能能夠根據(jù)用戶(hù)的“皮膚”首選項(xiàng)來(lái)決定是否采用水平的還是垂直的排

共2頁(yè)上一頁(yè)12下一頁(yè)
來(lái)源:模板無(wú)憂(yōu)//所屬分類(lèi):PHP教程/更新時(shí)間:2008-08-22
相關(guān)PHP教程