《PHP設計模式介紹》第五章 注冊模式(3)_PHP教程
推薦:《PHP設計模式介紹》第四章 單件模式幾乎所有面向對象的程序中,總有一兩個資源被創建出來,在程序應用中持續被共享使用。例如,這樣的一個資源,在一個電子商務程序的數據庫連接中使用:這個連接在應用程序啟動時初始化,程序于是
initRegistry()方法包含一個初始化為數組的靜態變量。這個靜態變量通過引用返回。在構造函數中$_store實例變量被賦于通過initRegistry()函數返回的引用——即靜態數組。好!一個PHP4的類靜態變量產生了。
使用類靜態變量的實現:
PHP5中,沒有必要自己實現類靜態變量,因為PHP5直接支持類靜態變量。因此,PHP5簡化了實現。而且,PHP5中引用、對象不再有PHP4中的意義,但是assertReference() 處理了這種差別,如果兩個變量指向同一個對象句柄也可以通過測試。
以下是為PHP5改寫的類似的Registry測試用例。
// PHP5 class RegistryMonoStatePHP5TestCase extends UnitTestCase { function testRegistryMonoState() { $this->assertCopy( $reg = new RegistryMonoState ,$reg2 = new RegistryMonoState); $this->assertFalse($reg->isValid(‘key’)); $this->assertNull($reg->get(‘key’)); $test_value = new TestObj; $reg->set(‘key’, $test_value); $this->assertReference($test_value, $reg2->get(‘key’)); } } |
以下是PHP5版本的使用靜態類變量的Registry類。
class RegistryMonoState { protected static $store = array(); function isValid($key) { return array_key_exists($key, RegistryMonoState::$store); } function get($key) { if (array_key_exists($key, RegistryMonoState::$store)) return RegistryMonoState::$store[$key]; } function set($key, $obj) { RegistryMonoState::$store[$key] = $obj; } } |
PHP5中用這種方式編碼Registry類的一個有趣的效果是你可以用相同的代碼使用實例或者靜態方法。以下是證明僅僅使用靜態方法的測試用例。
class RegistryMonoStatePHP5TestCase extends UnitTestCase { function testRegistryMonoState() { /*...*/ } function testRegistryMonoStateStaticCalls() { $this->assertFalse(RegistryMonoState::isValid(‘key’)); $this->assertNull(RegistryMonoState::get(‘key’)); $test_value = new TestObj; RegistryMonoState::set(‘key’, $test_value); $this->assertIdentical($test_value, RegistryMonoState::get(‘key’)); } |
現在你已經看到在PHP5中的靜態調用接口,下面讓我們在PHP4中實現相同的接口。在前面的PHP4“靜態類變量”部分,實現需要使用“函數靜態變量返回引用”來跟蹤。PHP4版本的靜態調用接口測試與PHP5版本的測試類似。
// PHP4 class RegistryStaticPHP4TestCase extends UnitTestCase { function testRegistryStatic() { $this->assertFalse(RegistryStatic::isValid(‘key’)); $this->assertNull(RegistryStatic::get(‘key’)); $test_value = ‘something’; RegistryStatic::set(‘key’, $test_value); $this->assertReference($test_value, RegistryStatic::get(‘key’)); } } |
以下是符合測試要求的代碼實現。
class RegistryStatic { function &_getRegistry() { static $store = array(); return $store; } function isValid($key) { $store =& RegistryStatic::_getRegistry(); return array_key_exists($key, $store); } function &get($key) { $store =& RegistryStatic::_getRegistry(); if (array_key_exists($key, $store)) return $store[$key]; } function set($key, &$obj) { $store =& RegistryStatic::_getRegistry(); $store[$key] =& $obj; } } |
這個實現方法的重點是getRegistry()方法返回一個對靜態數組的引用。
$store =& RegistryStatic::_getRegistry();這一行,在隨后的函數中把變量$store通過引用賦給靜態數組,允許所有的函數可以靜態訪問數組,允許所有的方法可以被靜態調用。
也可以不使用PHP4“靜態類變量跟蹤”達到相同的效果:將原先的基于單件模式的Registry類與一個包裝類結合以達到允許靜態調用。這個類與testRegistryStatic()有相同的測試代碼,但是他的實現如下所示:
class RegistryStatic { function isValid($key) { $reg =& Registry::getInstance(); return $reg->isValid($key); } function &get($key) { $reg =& Registry::getInstance(); return $reg->get($key); } function set($key, &$obj) { $reg =& Registry::getInstance(); $reg->set($key, $obj); } } |
結論:
雖然注冊模式簡化了對大量對象的訪問,但是仍然有許多問題——與全局變量聯合。你需要確定要求的屬性Key在訪問之已經被初始化了,而且設置屬性的方法可以全局訪問,你的對象仍然可能在你的代碼的其他部分出乎意料的被替換掉。顯然,全局數據非常有好處,方便,但是你需要時刻記住任何全局數據都是有一些不安全的。
內嵌的Registry模式
除了單獨使用注冊模式——如本章所示,Registry模式與其他對象結合時功能也是非常強大。例如:當對象的創建代價非常昂貴(例如需要查詢大量數據庫來初始化對象)時,而且對象在這個應用中被使用一次或多次,如果這樣,你能創建一個結合了工作模式 (見第三章) 和注冊模式 的“Finder”類以獲得已經創建的對象的緩存而不用再次創建他們?
以下是一個Contact類,AddressBook類是工廠類。
class AddressBook { function &findById($id) { return new Contact($id); } } class Contact { function Contact($id) { // expensive queries to create object using $id } // ... other methods } |
你可以在AddressBook類中插入Registry模式來提供緩存。代碼可以如下所示:
class AddressBook { var $registry; function AddressBook() { $this->registry =& Registry::getInstance(); } function &findById($id) { if (!$this->registry->isValid($id)) { $this->registry->set($id, new Contact($id)); } return $this->registry->get($id); } } |
AddressBook類的構造函數將registry綁定到一個實例變量。當創建了一個特定的ID并被findById()方法調用時,Registry被檢查以確定對象是否已經被緩存。如果沒有,將創建一個新的對象并存儲在Registry中。被調用的對象將通過函數從Registry中取出并被返回。
分享:《PHP設計模式介紹》第三章 工廠模式在面向對象編程中, 最通常的方法是一個new操作符產生一個對象實例,new操作符就是用來構造對象實例的。但是在一些情況下, new操作符直接生成對象會帶來一些問題。舉例來說, 許多類型對象的創造需
- 相關鏈接:
- 教程說明:
PHP教程-《PHP設計模式介紹》第五章 注冊模式(3)
。