《PHP設(shè)計(jì)模式介紹》第六章 偽對(duì)象模式(3)_PHP教程
推薦:《PHP設(shè)計(jì)模式介紹》第五章 注冊(cè)模式我們通常認(rèn)為避免使用全局變量是一種好的選擇,因此,對(duì)象經(jīng)常被作為參數(shù)從一段代碼傳遞到另一段。但是傳遞實(shí)例的一個(gè)問題就是對(duì)象有時(shí)候不知道將要傳遞給誰——?經(jīng)過一個(gè)函數(shù)后才被傳
這個(gè)“bootstrap”文件運(yùn)行新程序時(shí)應(yīng)如下所示:
| <?php require_once ‘classes.inc.php’; define(‘SELF’, ‘http://www.example.com/path/to/page.php’); $page =& new PageDirector(new Session, new Response); $page->run(); ?> |
該文件包含了所需的已定義類,并為自己定義了一個(gè)常量,給PageDirector類(其用于傳遞類Session 和類Response所依賴的實(shí)例來組成構(gòu)造函數(shù))創(chuàng)建了一個(gè)實(shí)例來執(zhí)行PageDirector::run()方法。
現(xiàn)在讓我們來創(chuàng)建一些測(cè)試的實(shí)例來詳細(xì)說明重構(gòu)后的運(yùn)用程序應(yīng)該有的功能。
| require_once ‘simpletest/unit_tester.php’; require_once ‘simpletest/reporter.php’; require_once ‘simpletest/mock_objects.php’; require_once ‘simpletest/web_tester.php’; require_once ‘classes.inc.php’; Session::init(); class PageWebTestCase extends WebTestCase { /*...*/ } class ResponseTestCase extends UnitTestCase { /*...*/ } class UserLoginTestCase extends UnitTestCase { /*...*/ } class SessionTestCase extends UnitTestCase { /*...*/ } class PageDirectorTestCase extends UnitTestCase { /*...*/ } $test = new GroupTest(‘Application PHP4 Unit Test’); $test->addTestCase(new PageWebTestCase); $test->addTestCase(new ResponseTestCase); $test->addTestCase(new UserLoginTestCase); $test->addTestCase(new SessionTestCase); $test->addTestCase(new PageDirectorTestCase); |
這段代碼或多或少的展示了一個(gè)典型的運(yùn)用程序的測(cè)試文件該是何種模樣。它一開始就包含了一些SimpleTest文件,也包括了用偽對(duì)象來測(cè)試的mock_object.php文件。接著,那些輔助類被包含進(jìn)來,方法Session::init()被調(diào)用,seesion開始。
緊接著的全是以“安全無害”為目標(biāo)而開始的測(cè)試實(shí)例,類WebTestCase確保所有程序按要求執(zhí)行, 然后是單獨(dú)的用于新設(shè)計(jì)的類的測(cè)試(盡管這種類本章不會(huì)詳述)。最后是我們接下去會(huì)討論的PageDirectorTestCase類。
類PageDirector的核心任務(wù)是協(xié)調(diào)類Session和類Response的對(duì)象,產(chǎn)生最終的網(wǎng)頁輸出結(jié)果。
| Mock::Generate(‘Session’); Mock::Generate(‘Response’); define(‘SELF’, ‘testvalue’); class PageDirectorTestCase extends UnitTestCase { // ... } |
在這段代碼的一開始,Mock::generate()創(chuàng)建了偽對(duì)象類的定義并定義了一個(gè)后面將要用到的常量。
假設(shè)對(duì)類Session 和類 Response的測(cè)試已經(jīng)存在,下一步就是創(chuàng)建偽Session來模擬類 Session的狀態(tài)。這個(gè)偽對(duì)象的設(shè)置和我們一開始所演示的例子極其類似。
因?yàn)镻ageDirector::run()方法正回顯內(nèi)容,你可以用輸出緩存內(nèi)容的辦法來捕獲它,看看是否正確。
| class PageDirectorTestCase extends UnitTestCase { // ... function TestLoggedOutContent() { $session =& new MockSession($this); $session->setReturnValue(‘get’, null, array(‘user_name’)); $session->expectOnce(‘get’, array(‘user_name’)); $page =& new PageDirector($session, new Response); ob_start(); $page->run(); $result = ob_get_clean(); $this->assertNoUnwantedPattern(‘/secret.*content/i’, $result); $this->assertWantedPattern(‘/<form.*<input[^>]*text[^>]*’ .’name.*<input[^>]*password[^>]*passwd/ims’ ,$result); $session->tally(); } } |
這段代碼證明了在SimpleTest中使用偽對(duì)象的必要性。我們來看看其中創(chuàng)建偽對(duì)象的一行代碼$session =&new MockSession($this)。你可以使用繼承自SimpleStub類(參見http://simpletest.sf.net/SimpleTest/MockObjects/SimpleStub.html#sec-methodsummary)的方法來創(chuàng)建你所希望的從對(duì)象(如同你在測(cè)試代碼時(shí)所做的那樣)返回的結(jié)果.下一步,實(shí)例化PageDirector類并用MockSession代替正式使用時(shí)的類來實(shí)例化相關(guān)代碼。
注:setReturnValue()方法
setReturnValue()方法通過指定當(dāng)偽對(duì)象的特定方法被調(diào)用時(shí)返回何值來讓偽對(duì)象以一個(gè)“替身”的身份融入代碼。已經(jīng)有了一些這種方法的變體:比如指定以一定次序返回一系列值的做法,還有以參數(shù)代替值來返回結(jié)果的做法。
expectOnce()方法
expectOnce()方法通過建立一些假想,這些假想是關(guān)于什么時(shí)候方法被調(diào)用以及多久調(diào)用一次,來允許你的偽對(duì)象以“批評(píng)者”的角色來測(cè)試代碼。這些假想當(dāng)你在測(cè)試中調(diào)用偽對(duì)象的tally()方法時(shí)會(huì)被報(bào)告。
| class PageDirector { var $session; var $response; function PageDirector(&$session, &$response) { $this->session =& $session; $this->response =& $response; } } |
因?yàn)镻ageDirector類認(rèn)為自己不是處于一個(gè)測(cè)試環(huán)境而是處于一個(gè)真實(shí)正常的運(yùn)用程序環(huán)境中,它回顯結(jié)果到瀏覽器。既然你實(shí)際上在測(cè)試時(shí)并不希望這個(gè)動(dòng)作,你就可以通過PHP輸出緩存的特性(參見http://php.net/outcontrol)來捕獲執(zhí)行時(shí)它往瀏覽器發(fā)送了什么。
| class PageDirector { // ... function run() { if (!$this->isLoggedIn()) { $this->showLogin(); } this->response->display(); } function isLoggedIn() { return ($this->session->get(‘user_name’)) ? true : false; } function showLogin() { $this->response->addBody(‘<form method=”post”>’); $this->response->addBody(‘Name:<input type=”text” name=”name”>’); $this->response->addBody(“\n”); $this->response->addBody( ‘Password:<input type=”password” name=”passwd”>’); $this->response->addBody(“\n”); $this->response->addBody(‘<input type=”submit” value=”Login”>’); $this->response->addBody(‘</form>’); } } |
下面的代碼段演示了緩存輸出被重構(gòu)為runPage方法的結(jié)果,它給人的感覺就像是當(dāng)用戶登錄時(shí)另一個(gè)對(duì)輸出的測(cè)試。
| class PageDirectorTestCase extends UnitTestCase { // ... function TestLoggedOutContent() { $session =& new MockSession($this); $session->setReturnValue(‘get’, null, array(‘user_name’)); $session->expectOnce(‘get’, array(‘user_name’)); $page =& new PageDirector($session, new Response); $result = $this->runPage($page); $this->assertNoUnwantedPattern(‘/secret.*content/i’, $result); $this->assertWantedPattern(‘/<form.*<input[^>]*text[^>]*’ .’name.*<input[^>]*password[^>]*passwd/ims’ ,$result); $session->tally(); } function TestLoggedInContent() { $session =& new MockSession($this); $session->setReturnValue(‘get’, ‘admin’, array(‘user_name’)); $session->expectAtLeastOnce(‘get’); $page =& new PageDirector($session, new Response); $result = $this->runPage($page); $this->assertWantedPattern(‘/secret.*content/i’, $result); $this->assertNoUnwantedPattern(‘/<form.*<input[^>]*text[^>]*’ .’name.*<input[^>]*password[^>]*passwd/ims’ ,$result); $session->tally(); } function runPage(&$page) { ob_start(); $page->run(); return ob_get_clean(); } } |
接下來,將加入一個(gè)檢查條件到PageDirector::run()方法來看看用戶是否已經(jīng)登錄并決定顯示什么模板:
| class PageDirector { // ... function run() { if ($this->isLoggedIn()) { $this->showPage( new UserLogin($this->session->get(‘user_name’))); } else { $this->showLogin(); } $this->response->display(); } function showPage(&$user) { $vars = array( ‘name’ => $user->name() ,’self’ => SELF ); $this->response->addBodyTemplate(‘page.tpl’, $vars); } } |
page.tpl看上去可能像這樣:
| Welcome <?php echo $name; ?> <br>Super secret member only content here. <a href=”<?php echo $self; ?>?clear”>Logout</a> |
此時(shí),MockSession扮演了ServerStub的角色來控制決定用戶是否登錄的條件。它的功能也類似評(píng)判者,決定這個(gè)信息是否通過如下兩個(gè)途徑被正確的使用:一個(gè)是明確地被預(yù)先定義并通過tally()被驗(yàn)證,另一個(gè)是不直接的生成正確的輸出,而是通過ServerStub返回的值來生成。
為了繼續(xù)重構(gòu)這段代碼,下一步要跳到前面的進(jìn)程。將要做兩個(gè)動(dòng)作:清除已經(jīng)登錄的用戶和驗(yàn)證登錄頁面提交的用戶名和密碼是否存在。
分享:《PHP設(shè)計(jì)模式介紹》第四章 單件模式幾乎所有面向?qū)ο蟮某绦蛑校傆幸粌蓚(gè)資源被創(chuàng)建出來,在程序應(yīng)用中持續(xù)被共享使用。例如,這樣的一個(gè)資源,在一個(gè)電子商務(wù)程序的數(shù)據(jù)庫連接中使用:這個(gè)連接在應(yīng)用程序啟動(dòng)時(shí)初始化,程序于是
- PHPNOW安裝Memcached擴(kuò)展方法詳解
- php記錄頁面代碼執(zhí)行時(shí)間
- PHP中獎(jiǎng)概率的抽獎(jiǎng)算法程序代碼
- apache設(shè)置靜態(tài)文件緩存方法介紹
- php對(duì)圖像的各種處理函數(shù)代碼小結(jié)
- PHP 關(guān)于訪問控制的和運(yùn)算符優(yōu)先級(jí)介紹
- 關(guān)于PHP語言構(gòu)造器介紹
- php/js獲取客戶端mac地址的實(shí)現(xiàn)代碼
- php5.5新數(shù)組函數(shù)array_column使用
- PHP preg_match的匹配多國(guó)語言的技巧
- php 中序列化和json使用介紹
- php采集文章中的圖片獲取替換到本地
- 相關(guān)鏈接:
- 教程說明:
PHP教程-《PHP設(shè)計(jì)模式介紹》第六章 偽對(duì)象模式(3)
。