這次給大家帶來PHP原型模式案例解析,使用PHP原型模式的注意事項有哪些,下面就是實戰案例,一起來看一下。
原型設計模式(Prototype Design Pattern)很有意思, 因為它使用了一種複製技術來複製執行個體化的對象. 新對象是通過複製原型執行個體來建立的. 在這裡, 執行個體是批執行個體化的具體類.原型設計模式的目的是通過使用複製來減少執行個體化對象的開銷.與其從一個類執行個體化新對象, 完全可以使用一個已有執行個體的複製.
複製函數
PHP中使用原型設計模式的關鍵是要瞭解如何使用內建函數clone()
.
<?phpabstract class CloneObject{ public $name; public $picture; abstract function clone();}class Boy extends CloneObject{ public function construct() { $this->face = "handsome"; $this->name = "chenqionghe"; } public function display() { echo 'look : '.$this->face;; echo '<br />'.$this->name.'<br />'; } public function clone() {}}$boy = new Boy();$boy->display();$cloneBoy = clone $boy;$cloneBoy->face = "still handsome";$cloneBoy->display();
運行結果如下
look : handsome
chenqionghe
look : still handsome
chenqionghe
$cloneBoy執行個體是通過複製Boy的執行個體$boy, 它可以像$boy一樣訪問相同的屬性, 而且像Boy類的直接執行個體一樣改變這些屬性.
注意: 對於所複製的執行個體 , clone關鍵字會為該執行個體的類執行個體化另一個執行個體(使用複製關鍵字可以建立一個類的副本, 如果可能, 會自動調用對象的clone
方法, 但不能直接調用 對象的clone
方法), 關於過程, 有一點需要注意的是, 複製不會不會啟動建構函式中的動作.
簡單的原型例子
我們以研究果蠅為例.研究的目標是建立一個原型果蠅, 然後一旦出現變異, 就構建這個變異果蠅
抽象類別介面和具體實現
原型(IPrototype)的兩個具體類實現分別表示不同性別的果蠅, 包括性別變數(gender)和不同性別的和行為.
IPrototype.php
<?phpabstract class IPrototype{ public $eyeColor; public $winBeat; public $unitEypes; abstract function clone();}
IPrototype的這兩個實現的區別體現在性別上, 性別用常量標識, 一個是MALE,另一個是FEMAIL.雄果蠅有一個$mated布爾變數, 雄果蠅交配後,這個布爾變數會設定為true, 雌果蠅有一個$fecundity變數,其中包含一個數字值, 表示這隻雄果蠅的繁殖能力(產卵個數):
MaleProto.php
<?phpinclude_once('IPrototype.php');class MaleProto extends IPrototype{ const gender = "MALE"; public $mated; public function construct() { $this->eyeColor = "red"; $this->winBeat = "220"; $this->unitEypes = "760"; } public function clone(){}}
FemaleProto.php
<?phpinclude_once('IPrototype.php');class FemaleProto extends IPrototype{ const gender = "FEMAIL"; public $fecundity; public function construct() { $this->eyeColor = "red"; $this->winBeat = "220"; $this->unitEypes = "760"; } public function clone(){}}
客戶
在原型設計模式中,Clien類確實是一個不可缺少的部分.原因在於, 儘管要將子類具體實現作為執行個體的模板,但使用相同的模板複製執行個體的具體工作是由Client類完成的
Client.php
<?phpfunction autoload($class_name){ include_once $class_name.'.php';}class Client{ //用於直接執行個體化 private $fly1; private $fly2; //用於複製 private $c1Fly; private $c2Fly; private $updatedCloneFly; public function construct() { //執行個體化 $this->fly1 = new MaleProto(); $this->fly2 = new FemaleProto(); //複製 $this->c1Fly = clone $this->fly1; $this->c2Fly = clone $this->fly2; $this->updatedCloneFly = clone $this->fly2; //更新複製 $this->c1Fly->mated = "true"; $this->c2Fly->fecundity = '186'; $this->updatedCloneFly->eyeColor = "purple"; $this->updatedCloneFly->winBeat = "220"; $this->updatedCloneFly->unitEyes = '750'; $this->updatedCloneFly->fecundity = '92'; //通過類型提示方法發送 $this->showFly($this->c1Fly); $this->showFly($this->c2Fly); $this->showFly($this->updatedCloneFly); } private function showFly(IPrototype $fly) { echo "Eye color: ".$fly->eyeColor."<br />"; echo "Wing Beats/second: ".$fly->winBeat."<br />"; echo "Eye units: ".$fly->unitEypes."<br />"; $genderNow = $fly::gender; echo "Gender: ".$genderNow."<br />"; if($genderNow == "FEMAIL") { echo "Number of eggs: ".$fly->fecundity."<hr />"; } else { echo "Mated: ".$fly->mated."<hr />"; } }}$worker = new Client();
運行結果如下
Eye color: red
Wing Beats/second: 220
Eye units: 760
Gender: MALE
Mated: trueEye color: red
Wing Beats/second: 220
Eye units: 760
Gender: FEMAIL
Number of eggs: 186Eye color: purple
Wing Beats/second: 220
Eye units: 760
Gender: FEMAIL
Number of eggs: 92
原型模式要依賴客戶通過 不念複製過程使用具體原型.在這個設計過程中, 客戶是完成複製的參與者, 由於複製是原型設計中的關鍵要素, 所以客戶是一個基本參與者, 而不僅僅是一個請求類.
現代企業組織
在建立型設計模式方面,現代企業組織就非常適合原型實現.現在企業組織往往是複雜而龐大的階層, 像物件導向編程一樣,要為很多共同的特徵建模.現在通過一個例子描述軟體工程公司.
軟體工程公司是一個典型的現代組織.工程部(Engineering Department)負責建立產品,管理部(Management)處理資源的協調組織,市場部(Marketing)負責產品的銷售,推廣和整體營銷.
介面中的封裝
在這個原型實現中,首先為程式的介面(一個抽象類別)增加OOP,與所有原型介面一樣,這個介面包含一個複製操作.另外它還包含一些抽象和具體的擷取方法和設定方法.其中有一個抽象擷取方法/設定方法對,但要由3個具體原型實現為這個抽象擷取方法/設定方法對提供具體實現.其他擷取方法和設定方法分分別應用於員工名,ID碼和照片等屬性.注意所有這些屬性都是保護屬性(protected),所以儘管具體的擷取方法和設定方法有公用可見度,但由於操作中使用的屬性具有保護和可見度,這就提供了某種程度的封裝:
<?phpabstract class IAcmePrototype{ protected $name; protected $id; protected $employeePic; protected $department; //部門 abstract function setDept($orgCode); abstract function getDept(); //名字 public function setName($emName) { $this->name = $emName; } public function getName() { return $this->name; } //ID public function setId($emId) { $this->id = $emId; } public function getId() { return $this->id; } //僱員映像 public function setPic($ePic) { $this->employeePic = $ePic; } public function getPic() { return $this->employeePic; } abstract function clone();}
利用這些擷取方法和設定方法, 所有屬性的值都通過繼承的保護變數來設定.採用這種設計, 擴充類及其執行個體可以得到更好的封裝.
介面實現
3個IAcmePrototype子類都必須實現"dept"抽象方法以及clone()
方法.類似地, 每個具體原型類還包含一個常量UNIT,它提供一個賦值,可以由執行個體(包括複製的對象)作為標識
首先來看市場部的Marketing類:
Marketing.php
<?phpinclude_once('IAcmePrototype.php');class Marketing extends IAcmePrototype{ const UNIT = "Marketing"; private $sales = "sales"; private $promotion = "promotion"; private $strategic = "strategic planning"; public function setDept($orgCode) { switch($orgCode) { case 101: $this->department = $this->sales; break; case 102: $this->department = $this->promotion; break; case 103: $this->department = $this->strategic; break; default : $this->department = "未識別的市場部"; } } public function getDept() { return $this->department; } public function clone() {}}
setDept()
方法的實現使用了一個參數.並不是直接輸入市場部的部門,這個方法的參數是一個數字碼, 通過一個switch語句,限制了3種可接受的情況和預設情況,別外兩個原型實現也類似
Management.php
<?phpinclude_once('IAcmePrototype.php');class Management extends IAcmePrototype{ const UNIT = "Management"; private $research = "research"; private $plan = "planning"; private $operations = "operations"; public function setDept($orgCode) { switch($orgCode) { case 201: $this->department = $this->research; break; case 202: $this->department = $this->plan; break; case 203: $this->department = $this->operations; break; default : $this->department = "未識別的管理部"; } } public function getDept() { return $this->department; } public function clone() {}}
Engineering.php
<?phpinclude_once('IAcmePrototype.php');class Engineering extends IAcmePrototype{ const UNIT = "Engineering"; private $development = "development"; private $design = "design"; private $sysAd = "system administration"; public function setDept($orgCode) { switch($orgCode) { case 301: $this->department = $this->development; break; case 302: $this->department = $this->design; break; case 303: $this->department = $this->sysAd; break; default : $this->department = "未識別的工程部"; } } public function getDept() { return $this->department; } public function clone() {}}
以上這3個具體原型實現分別有其特定用途,不過它們都符合介面,可以建立各個原型實現的一個執行個體, 然後根據需要複製多個執行個體.這個複製的工作由Client類完成
客戶
客戶的設定非常簡單: 分別建立各個具體原型的一個執行個體, 然後按以下列表來複製各個執行個體:
市場部門執行個體:
-----市場部門複製
-----市場部門複製
管理部門執行個體
-----管理部門複製
工程部門執行個體
-----工程部門複製
-----工程部門複製
將來只使用這些複製對象.使用擷取方法和設定方法將各個特定情況的資訊賦給這些複製對象.以下是Client的實現
Client.php
<?phpfunction autoload($class_name){ include_once $class_name.'.php';}class Client{ private $market; private $manage; private $engineer; public function construct() { $this->makeConProto(); $Tess = clone $this->market; $this->setEmployee($Tess, "Tess Smith" , 101, 'ts101-1234', 'tess.png'); $this->showEmployee($Tess); $Jacob = clone $this->market; $this->setEmployee($Jacob, "Jacob Jones" , 101, 'jj101-2234', 'jacob.png'); $this->showEmployee($Jacob); $Ricky = clone $this->manage; $this->setEmployee($Ricky, "Ricky Rodriguez" , 203, 'rr101-5634', 'ricky.png'); $this->showEmployee($Ricky); $Olivaia = clone $this->engineer; $this->setEmployee($Olivaia, "Olivaia Perez" , 302, 'op301-1278', 'olivia.png'); $this->showEmployee($Olivaia); $John = clone $this->engineer; $this->setEmployee($John, "John Jacson" , 101, 'jj301-14548', 'john.png'); $this->showEmployee($John); } private function makeConProto() { $this->market = new Marketing(); $this->manage = new Management(); $this->engineer = new Engineering(); } private function showEmployee(IAcmePrototype $employeeNow) { $px = $employeeNow->getPic(); echo "<img src=$px width='150' height='150' /><br />"; echo $employeeNow->getName().'<br />'; echo $employeeNow->getDept().':'.$employeeNow::UNIT.'<br />'; echo $employeeNow->getId().'<hr />'; } private function setEmployee(IAcmePrototype $employeeNow, $nm, $dp, $id, $px) { $employeeNow->setName($nm); $employeeNow->setDept($dp); $employeeNow->setId($id); $employeeNow->setPic($px); }}$worker = new Client();
解釋:
客戶Client的建構函式類包含3個私人屬性, 用來分別執行個體化3個具體原型類. makeConPro()
方法產生必要的執行個體.
接下來,使用複製技術建立一個"員工"執行個體.然後,這個執行個體向一個設定方法setEmployee()發送特定的執行個體資訊,這個設定方法使用IAcmePrototype介面類型提示,不過需要說明, 它只對第一個參數使用類型提示,其他參數都沒有類型提示, 並不要求它們派生自IAcmePrototype介面.複製"員工"可以使用IAcmePrototype抽象類別的所有設定方法以及具體原型類實現的setDept()方法.
要使用各個員工的資料,Client類可以使用繼承的擷取方法.以下是運行Client輸出的結果
Tess Smith
sales:Marketing
ts101-1234
Jacob Jones
sales:Marketing
jj101-2234
Ricky Rodriguez
operations:Management
rr101-5634
Olivaia Perez
design:Engineering
op301-1278
John Jacson
未識別的工程部:Engineering
jj301-14548
可以根據需要增加更多的複製, 而且只需要對具體原型類完成一次執行個體化.使用原型模式時, 並不是建立具體類的多個執行個體,而只需要一個類執行個體化和多個複製.
完成修改,增加特性
要記住,最重要(可能也是最基本)的是, 設計模式允許開發人員修改和增補程式,而不必一切從頭開始.例如, 假設總裁決定公司增加一個新的部門,比如研究部門(Research), 這會很難嗎?一點也不難.Research可以擴充IAcmePrototype抽象類別, 然後實現抽象擷取方法和設定方法來反映這個研究部門的組織.需要說明,Client類中擷取方法和設定方法使用的代碼提示指示一個介面,而不是一個抽象類別的具體實現.所以, 只要增加的單元正確地實現了這個介面,就能順利地增加到應用中, 而不會帶來波動,也不需要對程式中的其他參與者進行重構.
不僅可以增加更多的具體類, 還可以很容易地對各個類進行修改, 而不會造成破壞.例如假設這個組織的市場部決定,除了現有的部門外, 他們還需要一個特殊的線上市場部,. 這樣一來, switch/case操作需要一個新的分支(case), 還要有一個新的私人屬性(變數)來描述新增的這個部門.這個改變將封凍在單獨的類中, 而不會影響其他參與者.由於這種改變不會帶來破壞, 所以應用的規模越大, 這一點就越重要.可以看到原型設計模式不僅支援一致性, 還支援靈活的改變.
PHP世界中的原型
由於PHP是一個伺服器端語言,也是與MySQL資料庫互動的一個重要工具,所以原型設計模式尤其適用 .並不是為資料庫的第一個元素分別建立對象,PHP可以使用原型模式建立具體類的一個執行個體,然後利用資料庫中的資料複製其餘的執行個體(記錄).
瞭解複製過程之後,與直接執行個體化的過程相比,你可能會問:"這有什麼區別?" 換句話說,為什麼複製比直接執行個體化對象需要的資源少?它們的區別並不能直接看到. 一個對象通過複製建立執行個體時, 它不會啟動建構函式.複製能得到原始類的所有屬性, 甚至還包含父介面的屬性,另外還繼承了傳遞執行個體化對象的所有值.建構函式產生的所有值以及儲存在對象屬性中的值都會成為複製對象的一部分.所以沒有返回建構函式.如果發現你的複製對象確實需要訪問建構函式產生的值但又無法訪問, 這說明需要對類進行重構,使執行個體能擁有它們需要的一切資訊, 而且可以把這些資料傳遞給複製對象.
總的來說, 原型模式在很多不同類型的PHP項目中都很適用, 如果解決一個問題需要乃至建立型模式, 都可以使用原型模式.
相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!
推薦閱讀:
PHP封閉原則(OCP)使用案例解析
PHP依賴倒置案例詳解