原型模式是一種創建者模式,其特點在於透過「複製」一個已經存在的實例來傳回新的實例,而不是新實例。本文將針對原型模式進行實例詳解,希望對大家有幫助。
原型模式中主要角色
抽象原型(Prototype)角色:宣告一個複製自己的介面
特定原型(Concrete Prototype)角色:實作一個克隆自己的操作
當一個類別大部分都是相同的只有部分是不同的時候,如果需要大量這個類別的對象,每次都重複實例化那些相同的部分是開銷很大的,而如果clone之前建立物件的那些相同的部分,就可以節約開銷。
針對php的實作方式就是__construct()和initialize函數分開分別處理這個類別的初始化,construct裡面放prototype也就是公共的部分,initialize裡面是每個物件特殊的部分。這樣我們先建立一個類別不initialize,以後每次clone這個類別再進行initialize就可以了。
在zend framework官方手冊裡面提到了這個http://framework.zend.com/manual/2.0/en/user-guide/database-and-models.html,但是沒有細講,下面我來分析一下
一、引入
在zf2的model裡面有一個albumTable類,相當於一個操作資料庫動作的助手類,裡面用到了tablegateway。
為了每次初始化albumtable都是相同的一個類,將初始化工作放到了根目錄的module.php檔案的getServiceConfig(),其中用到工廠模式,並且透過回呼函數,當每次ServiceManager ($sm)需要實例化一個物件的時候會自動呼叫建立一個alumTable。下面程式碼我們可以看出,建立一個albumTable也需要用相同的方式建立一個AlbumTableGateWay,這個類別就用到了我們所要講的原型模式。
二、程式碼詳解
public function getServiceConfig() { return array( 'factories' => array( 'Album\Model\AlbumTable' => function($sm) { $tableGateway = $sm->get('AlbumTableGateway'); $table = new AlbumTable($tableGateway); return $table; }, 'AlbumTableGateway' => function ($sm) { $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter'); $resultSetPrototype = new ResultSet(); $resultSetPrototype->setArrayObjectPrototype(new Album());//这个就是一个不变的原型 return new TableGateway('album', $dbAdapter, null, $resultSetPrototype);//传入到TableGateWay的构造函数中去 }, ), ); }
注意並不是TableGateWay運用了原型模式而是ResultSet這個類別運用了。每當tablegateway呼叫select()或insert()等方法的時候都會建立一個ResultSet用來表示結果,這些ResultSet中公共部分被clone,而獨特的部分類別如data就會被initialize。
三、更多程式碼範例
為了更清晰得了解這個原型,我們先拋開zend這個大框架,看一個完整的程式碼範例。範例來自
PHP Constructor Best Practices And The Prototype Pattern
這篇文章關於prototype pattern的部分前半部分其實是混雜怎樣在構造函數中運用繼承來提高擴展性,兩個模式看起來可能不太好理解,我們直接看最後的程式碼關於prototype pattern的部分。
<?php //框架中很常见的adapter类,用来适配各种数据库,封装一些基本数据库连接操作。 //相当于上面代码中的adapter类 class DbAdapter { public function fetchAllFromTable($table) { return $arrayOfData; } } //运用prototype pattern的类,注意construct和initialize是分开的 //相当于上面zend 代码里面的ResultSet类 class RowGateway { public function __construct(DbAdapter $dbAdapter, $tableName) { $this->dbAdapter = $dbAdapter; $this->tableName = $tableName; } public function initialize($data) { $this->data = $data; } /** * Both methods require access to the database adapter * to fulfill their duties */ public function save() {} public function delete() {} public function refresh() {} } //相当于上面代码中的TableGateway类,关于gateway可以具体去了解一下。 class UserRepository { public function __construct(DbAdapter $dbAdapter, RowGateway $rowGatewayPrototype = null) { $this->dbAdapter = $dbAdapter; $this->rowGatewayPrototype = ($rowGatewayPrototype) ? new RowGateway($this->dbAdapter, 'user') } public function getUsers() { $rows = array(); foreach ($this->dbAdapter->fetchAllFromTable('user') as $rowData) { $rows[] = $row = clone $this->rowGatewayPrototype; $row->initialize($rowData); } return $rows; } }
這幾個類別其實和上面zend程式碼中的類別是對應的
Dbadapter -- adpater
RowGateWay -- ResultSet
UserRepository - TableGateWay
具體看程式碼中的註解。
這裡的RowGateWay可以很明顯的看出在getusers中需要大量的實例化,那麼原型模式就是很必要的了。
下面是運用這個類別的程式碼
class ReadWriteRowGateway extends RowGateway { public function __construct(DbAdapter $readDbAdapter, DbAdapter $writeDbAdapter, $tableName) { $this->readDbAdapter = $readDbAdapter; parent::__construct($writeDbAdapter, $tableName); } public function refresh() { // utilize $this->readDbAdapter instead of $this->dbAdapter in RowGateway base implementation } } // usage: $userRepository = new UserRepository( $dbAdapter, new ReadWriteRowGateway($readDbAdapter, $writeDbAdapter, 'user') ); $users = $userRepository->getUsers(); $user = $users[0]; // instance of ReadWriteRowGateway with a specific row of data from the db
相關推薦:
################################################# ##########php設計模式之觀察者模式詳解#########以上是php之Constructor Prototype Pattern 原型模式詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!