《PHP設(shè)計(jì)模式介紹》第五章 注冊(cè)模式(2)_PHP教程
推薦:《PHP設(shè)計(jì)模式介紹》第四章 單件模式幾乎所有面向?qū)ο蟮某绦蛑�,總有一兩個(gè)資源被創(chuàng)建出來,在程序應(yīng)用中持續(xù)被共享使用。例如,這樣的一個(gè)資源,在一個(gè)電子商務(wù)程序的數(shù)據(jù)庫連接中使用:這個(gè)連接在應(yīng)用程序啟動(dòng)時(shí)初始化,程序于是
測(cè)試又通過了!現(xiàn)在我們想最終特性進(jìn)發(fā):給定一個(gè)屬性key,注冊(cè)模式類的get()方法將返回一個(gè)對(duì)特定對(duì)象的引用。一下為符合這一要求的測(cè)試用例。
代碼:
| class RegistryPHP4TestCase extends UnitTestCase {function testRegistryIsSingleton() { /*...*/ } function testEmptyRegistryKeyIsInvalid() { /*...*/ } function testEmptyRegistryKeyReturnsNull() { /*...*/ } function testSetRegistryKeyBecomesValid() { /*...*/ } function testSetRegistryValueIsReference() {$reg =& Registry::getInstance();$test_value = 'something'; $reg->set('key', $test_value); $this->assertReference($test_value, $reg->get('key')); //another way to test the reference $test_value .= ' else'; $this->assertEquual('something else',$reg->get('key')); } } |
以下為注冊(cè)模式類的完整實(shí)現(xiàn)代碼。
代碼:
| class Registry {var $_store = array(); function isValid($key) {return array_key_exists($key, $this->_store);} function &get($key) {if (array_key_exists($key, $this->_store)) return $this->_store[$key];} function set($key, &$obj) {$this->_store[$key] =& $obj;} function &getInstance() {static $instance = array(); if (!$instance) $instance[0] =& new Registry; return $instance[0]; } } |
“注冊(cè)模式”的get()方法會(huì)返回一個(gè)對(duì)象引用。類似的,set()方法的$obj參數(shù)要求得到一個(gè)對(duì)象引用并被賦值$this->_store[$key].。get()和set()方法的聯(lián)合恰當(dāng)使用能夠滿足assertReference()測(cè)試。
作者注:
“注冊(cè)模式”的getRegistry::get()方法的代碼應(yīng)該寫成@$this->_store[$key;]的形式,但是最好避免使用錯(cuò)誤抑制符,使用錯(cuò)誤抑制符的代碼會(huì)變的摸棱兩可,你需要花費(fèi)額外的時(shí)間去了解你是否會(huì)再次訪問這段代碼。array_key_exists()方法指出了應(yīng)該避免的錯(cuò)誤。
PHP5中,對(duì)象句柄(引用)帶來了革命性的變化——你可以從對(duì)象引用的困境中解脫出來。事實(shí)上PHP5中注冊(cè)模式的實(shí)現(xiàn)變的簡(jiǎn)單多了。因?yàn)槟阍僖膊挥脫?dān)心因?yàn)闆]有通過引用傳遞對(duì)象而引起致命錯(cuò)誤的情況下使用聯(lián)合數(shù)組。在PHP5中,你甚至能在注冊(cè)模式中混和使用對(duì)象和變量。
一個(gè)例子:
在實(shí)際應(yīng)用中“注冊(cè)模式”會(huì)是什么樣子?在網(wǎng)絡(luò)應(yīng)用程序開發(fā)中,通常我們只擁有一個(gè)數(shù)據(jù)庫連接。(因此,廣泛使用“單一模式”管理數(shù)據(jù)連接)但是,比如,由于歷史遺留原因:你的應(yīng)用的客戶數(shù)據(jù)庫與你的在線訂單數(shù)據(jù)庫是分開的,你的DBA又把你的舊訂單轉(zhuǎn)移到一個(gè)存檔數(shù)據(jù)庫中,而且它與你的客戶數(shù)據(jù)庫及訂單(現(xiàn)有,最近)數(shù)據(jù)庫也是完全隔離的。那么,你怎么才能方便地管理三個(gè)數(shù)據(jù)庫連接而不用創(chuàng)建三個(gè)單獨(dú)的“單一模式”呢?答安就是使用“注冊(cè)模式”。
代碼:
| class DbConnections extends Registry {} |
注:當(dāng)你在你的代碼中引入設(shè)計(jì)模式時(shí),你的類名應(yīng)該仍能反映他的角色和功能而沒有必要使用模式的名字。使用模式的名字注釋代碼對(duì)與你的項(xiàng)目以外的程序員交流非常有幫助。但是在你的項(xiàng)目?jī)?nèi),類的名字應(yīng)該適合項(xiàng)目本身而且能夠被項(xiàng)目成員很好的理解。雖然本章范例中的類名反映了設(shè)計(jì)模式的名字以及特定的實(shí)現(xiàn)方式,但是這并不是必須的。這僅僅是為了例子的清晰明了而不是好的命名規(guī)范。
DbConnections類是一個(gè)單件模式類,又繼承了注冊(cè)模式——DbConnections綜合了兩者的優(yōu)點(diǎn)。
以下的代碼片斷創(chuàng)建并在注冊(cè)模式類中存儲(chǔ)了對(duì)每一個(gè)數(shù)據(jù)庫的連接。
代碼:
| //initial setup, somewhere near the start of your script $dbc =& DbConnections::getInstance(); $dbc->set( 'contacts', new MysqlConnection('user1', 'pass1', 'db1', 'host1')); $dbc->set( 'orders', new MysqlConnection('user2', 'pass2', 'db2', 'host2')); $dbc->set( 'archives', new MysqlConnection('user3', 'pass3', 'db3', 'host3')); |
在其他類中將注冊(cè)模式類連同數(shù)據(jù)一起載入就可以使用不同的連接了。
代碼:
| // domain model classes class Customer { var $db; function Customer() { $dbc =& DbConnections::getInstance(); $this->db =& $dbc->get('contacts'); } //... } class Orders { var $db_cur; var $db_hist; function Contact() { $dbc =& DbConnections::getInstance(); $this->db_cur =& $dbc->get('orders'); $this->db_hist =& $dbc->get('archive'); } //... } |
一個(gè)類依據(jù)客戶數(shù)據(jù)庫建模,另一個(gè)類依據(jù)歷史和現(xiàn)在的客戶訂單建模。取得正確的數(shù)據(jù)庫鏈接需要兩個(gè)步驟:找到注冊(cè)模式類,從中找出與給定的屬性(key)相匹配的對(duì)象。
將注冊(cè)模式實(shí)現(xiàn)為單件模式:
如前所述,把注冊(cè)模式實(shí)現(xiàn)為單件模式有很多實(shí)現(xiàn)方式。
第一步,將注冊(cè)模式實(shí)現(xiàn)為單件對(duì)象,(作者注:我們?cè)诘谒恼?mdash;—The Singleton Pattern末尾簡(jiǎn)單討論過)。
按照這種設(shè)計(jì),注冊(cè)模式類的任何一個(gè)實(shí)例都將訪問同一個(gè)數(shù)組。我們把這個(gè)新類叫做RegistryGlobal以區(qū)別于我們前面開發(fā)的類,并反映這種實(shí)現(xiàn)方式的特性。
以下為反映這種思想的測(cè)試用例(它應(yīng)該看起來很熟悉)。
代碼:
| class RegistryGlobalPHP4TestCase extends UnitTestCase { function testRegistryGlobal() { $reg =& new RegistryGlobal; $this->assertFalse($reg->isValid('key')); $this->assertNull($reg->get('key')); $test_value = 'something'; $reg->set('key', $test_value); $this->assertReference($test_value, $reg->get('key')); } } |
實(shí)現(xiàn)代碼如下所示:
| class RegistryGlobal { var $_store = array(); function isValid($key) { return array_key_exists($key, $this->_store); } function &get($key) { if (array_key_exists($key, $this->_store)) return $this->_store[$key]; } function set($key, &$obj) { $this->_store[$key] =& $obj; } } |
isValid(), get(),和set()方法與我們前面開發(fā)的注冊(cè)模式類完全相同。
下一步:我們來編寫驗(yàn)證RegistryGlobal類是單件模式的測(cè)試用例。
代碼:
| class RegistryGlobalPHP4TestCase extends UnitTestCase { function testRegistryGlobal() { /*...*/ } function testRegistryGlobalIsMonoState() { $reg =& new RegistryGlobal; $reg2 =& new RegistryGlobal; $this->assertCopy($reg, $reg2); $test_value = 'something'; $reg->set('test', $test_value); $this->assertReference( $reg->get('test') ,$reg2->get('test')); } } |
這里測(cè)試用例創(chuàng)建了RegistryGlobal類的兩個(gè)實(shí)例,并確認(rèn)他們不是對(duì)同一對(duì)象的引用——在一個(gè)實(shí)例內(nèi)設(shè)置一個(gè)對(duì)象的屬性值(value),最后證實(shí)兩個(gè)實(shí)例返回相同的對(duì)象。若測(cè)試通過RegistryGlobal類就擁有單態(tài)的行為。
代碼:
| define('REGISTRY_GLOBAL_STORE', '__registry_global_store_key__'); class RegistryGlobal {var $_store; function RegistryGlobal() {if (!array_key_exists(REGISTRY_GLOBAL_STORE, $GLOBALS)||!is_array($GLOBALS[REGISTRY_GLOBAL_STORE])) {$GLOBALS[REGISTRY_GLOBAL_STORE] = array(); } $this->_store =& $GLOBALS[REGISTRY_GLOBAL_STORE]; } function isValid($key) {return array_key_exists($key, $this->_store);} function &get($key) {if (array_key_exists($key, $this->_store)) return $this->_store[$key];} function set($key, &$obj) { $this->_store[$key] =& $obj; } } |
本方法中的神奇之處在于$this->_store =& $GLOBALS[REGISTRY_GLOBAL_STORE;] 這一行,引用操作符將全局?jǐn)?shù)組綁定到實(shí)例變量$_store上。這是單件模式實(shí)現(xiàn)的關(guān)鍵所在:每次在對(duì)象中使用$this->_store變量時(shí),作用反映到全局變量中。
但是并不推薦基于全局變量的解決方案。如果PHP4支持這一特性的話,靜態(tài)類變量會(huì)是更好的解決方案。然而,我們可以在代碼中通過引用實(shí)現(xiàn)靜態(tài)類變量嗎?
測(cè)試與 RegistryGlobal 類的測(cè)試相似。
| class RegistryMonoStatePHP4TestCase extends UnitTestCase { function testRegistryMonoState() { $this->assertCopy( $reg =& new RegistryMonoState; $reg2 =& new RegistryMonoState); $this->assertFalse($reg->isValid(‘key’)); $this->assertNull($reg->get(‘key’)); $test_value = ‘something’; $reg->set(‘key’, $test_value); $this->assertReference($reg->get(‘key’), $reg2->get(‘key’)); } } |
要自己實(shí)現(xiàn)類靜態(tài)變量,可以將一個(gè)對(duì)函數(shù)靜態(tài)變量的引用綁定到類的實(shí)例變量上。
| class RegistryMonoState {var $_store; function &_initRegistry() { static $store = array(); return $store; } function RegistryMonoState() { $this->_store =& $this->_initRegistry(); } function isValid($key) { return array_key_exists($key, $this->_store); } function &get($key) { if (array_key_exists($key, $this->_store)) return $this->_store[$key]; } function set($key, &$obj) { $this->_store[$key] =& $obj; } } |
分享:《PHP設(shè)計(jì)模式介紹》第三章 工廠模式在面向?qū)ο缶幊讨? 最通常的方法是一個(gè)new操作符產(chǎn)生一個(gè)對(duì)象實(shí)例,new操作符就是用來構(gòu)造對(duì)象實(shí)例的。但是在一些情況下, new操作符直接生成對(duì)象會(huì)帶來一些問題。舉例來說, 許多類型對(duì)象的創(chuàng)造需
- 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的匹配多國語言的技巧
- php 中序列化和json使用介紹
- php采集文章中的圖片獲取替換到本地
- 相關(guān)鏈接:
- 教程說明:
PHP教程-《PHP設(shè)計(jì)模式介紹》第五章 注冊(cè)模式(2)
。