本文實例講述了PHP設(shè)計模式之原型模式定義與用法。分享給大家供大家參考,具體如下:
原型設(shè)計模式(Prototype Design Pattern)很有意思, 因為它使用了一種克隆技術(shù)來復(fù)制實例化的對象. 新對象是通過復(fù)制原型實例來創(chuàng)建的. 在這里, 實例是批實例化的具體類.原型設(shè)計模式的目的是通過使用克隆來減少實例化對象的開銷.與其從一個類實例化新對象, 完全可以使用一個已有實例的克隆.
克隆函數(shù)
PHP中使用原型設(shè)計模式的關(guān)鍵是要了解如何使用內(nèi)置函數(shù)__clone()
.
02 | abstract class CloneObject |
06 | abstract function __clone(); |
08 | class Boy extends CloneObject |
10 | public function __construct() |
12 | $this ->face = "handsome" ; |
13 | $this ->name = "chenqionghe" ; |
15 | public function display() |
17 | echo 'look : ' . $this ->face;; |
18 | echo '<br />' . $this ->name. '<br />' ; |
20 | public function __clone() {} |
24 | $cloneBoy = clone $boy ; |
25 | $cloneBoy ->face = "still handsome" ; |
運行結(jié)果如下
look : handsome
chenqionghe
look : still handsome
chenqionghe
$cloneBoy實例是通過克隆Boy的實例$boy, 它可以像$boy一樣訪問相同的屬性, 而且像Boy類的直接實例一樣改變這些屬性.
注意: 對于所克隆的實例 , clone關(guān)鍵字會為該實例的類實例化另一個實例(使用克隆關(guān)鍵字可以創(chuàng)建一個類的副本, 如果可能, 會自動調(diào)用對象的__clone
方法, 但不能直接調(diào)用 對象的__clone
方法), 關(guān)于過程, 有一點需要注意的是, 克隆不會不會啟動構(gòu)造函數(shù)中的動作.
簡單的原型例子
我們以研究果蠅為例.研究的目標(biāo)是建立一個原型果蠅, 然后一旦出現(xiàn)變異, 就構(gòu)建這個變異果蠅
抽象類接口和具體實現(xiàn)
原型(IPrototype)的兩個具體類實現(xiàn)分別表示不同性別的果蠅, 包括性別變量(gender)和不同性別的和行為.
IPrototype.php
2 | abstract class IPrototype |
7 | abstract function __clone(); |
IPrototype的這兩個實現(xiàn)的區(qū)別體現(xiàn)在性別上, 性別用常量標(biāo)識, 一個是MALE,另一個是FEMAIL.雄果蠅有一個$mated布爾變量, 雄果蠅交配后,這個布爾變量會設(shè)置為true, 雌果蠅有一個$fecundity變量,其中包含一個數(shù)字值, 表示這只雄果蠅的繁殖能力(產(chǎn)卵個數(shù)):
MaleProto.php
02 | include_once ( 'IPrototype.php' ); |
03 | class MaleProto extends IPrototype |
05 | const gender = "MALE" ; |
07 | public function __construct() |
09 | $this ->eyeColor = "red" ; |
10 | $this ->winBeat = "220" ; |
11 | $this ->unitEypes = "760" ; |
13 | public function __clone(){} |
FemaleProto.php
02 | include_once ( 'IPrototype.php' ); |
03 | class FemaleProto extends IPrototype |
05 | const gender = "FEMAIL" ; |
07 | public function __construct() |
09 | $this ->eyeColor = "red" ; |
10 | $this ->winBeat = "220" ; |
11 | $this ->unitEypes = "760" ; |
13 | public function __clone(){} |
客戶
在原型設(shè)計模式中,Clien類確實是一個不可缺少的部分.原因在于, 盡管要將子類具體實現(xiàn)作為實例的模板,但使用相同的模板克隆實例的具體工作是由Client類完成的
Client.php
02 | function __autoload( $class_name ) |
04 | include_once $class_name . '.php' ; |
14 | private $updatedCloneFly ; |
15 | public function __construct() |
18 | $this ->fly1 = new MaleProto(); |
19 | $this ->fly2 = new FemaleProto(); |
21 | $this ->c1Fly = clone $this ->fly1; |
22 | $this ->c2Fly = clone $this ->fly2; |
23 | $this ->updatedCloneFly = clone $this ->fly2; |
25 | $this ->c1Fly->mated = "true" ; |
26 | $this ->c2Fly->fecundity = '186' ; |
27 | $this ->updatedCloneFly->eyeColor = "purple" ; |
28 | $this ->updatedCloneFly->winBeat = "220" ; |
29 | $this ->updatedCloneFly->unitEyes = '750' ; |
30 | $this ->updatedCloneFly->fecundity = '92' ; |
32 | $this ->showFly( $this ->c1Fly); |
33 | $this ->showFly( $this ->c2Fly); |
34 | $this ->showFly( $this ->updatedCloneFly); |
36 | private function showFly(IPrototype $fly ) |
38 | echo "Eye color: " . $fly ->eyeColor. "<br />" ; |
39 | echo "Wing Beats/second: " . $fly ->winBeat. "<br />" ; |
40 | echo "Eye units: " . $fly ->unitEypes. "<br />" ; |
41 | $genderNow = $fly ::gender; |
42 | echo "Gender: " . $genderNow . "<br />" ; |
43 | if ( $genderNow == "FEMAIL" ) |
45 | echo "Number of eggs: " . $fly ->fecundity. "<hr />" ; |
49 | echo "Mated: " . $fly ->mated. "<hr />" ; |
53 | $worker = new Client(); |
運行結(jié)果如下
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
原型模式要依賴客戶通過 不念克隆過程使用具體原型.在這個設(shè)計過程中, 客戶是完成克隆的參與者, 由于克隆是原型設(shè)計中的關(guān)鍵要素, 所以客戶是一個基本參與者, 而不僅僅是一個請求類.
現(xiàn)代企業(yè)組織
在創(chuàng)建型設(shè)計模式方面,現(xiàn)代企業(yè)組織就非常適合原型實現(xiàn).現(xiàn)在企業(yè)組織往往是復(fù)雜而龐大的層次結(jié)構(gòu), 像面向?qū)ο缶幊桃粯?要為很多共同的特征建模.現(xiàn)在通過一個例子描述軟件工程公司.
軟件工程公司是一個典型的現(xiàn)代組織.工程部(Engineering Department)負責(zé)創(chuàng)建產(chǎn)品,管理部(Management)處理資源的協(xié)調(diào)組織,市場部(Marketing)負責(zé)產(chǎn)品的銷售,推廣和整體營銷.
接口中的封裝
在這個原型實現(xiàn)中,首先為程序的接口(一個抽象類)增加OOP,與所有原型接口一樣,這個接口包含一個克隆操作.另外它還包含一些抽象和具體的獲取方法和設(shè)置方法.其中有一個抽象獲取方法/設(shè)置方法對,但要由3個具體原型實現(xiàn)為這個抽象獲取方法/設(shè)置方法對提供具體實現(xiàn).其他獲取方法和設(shè)置方法分分別應(yīng)用于員工名,ID碼和照片等屬性.注意所有這些屬性都是保護屬性(protected),所以盡管具體的獲取方法和設(shè)置方法有公共可見性,但由于操作中使用的屬性具有保護和可見性,這就提供了某種程度的封裝:
02 | abstract class IAcmePrototype |
06 | protected $employeePic ; |
07 | protected $department ; |
09 | abstract function setDept( $orgCode ); |
10 | abstract function getDept(); |
12 | public function setName( $emName ) |
14 | $this ->name = $emName ; |
16 | public function getName() |
21 | public function setId( $emId ) |
25 | public function getId() |
30 | public function setPic( $ePic ) |
32 | $this ->employeePic = $ePic ; |
34 | public function getPic() |
36 | return $this ->employeePic; |
38 | abstract function __clone(); |
利用這些獲取方法和設(shè)置方法, 所有屬性的值都通過繼承的保護變量來設(shè)置.采用這種設(shè)計, 擴展類及其實例可以得到更好的封裝.
接口實現(xiàn)
3個IAcmePrototype子類都必須實現(xiàn)"dept"抽象方法以及__clone()
方法.類似地, 每個具體原型類還包含一個常量UNIT,它提供一個賦值,可以由實例(包括克隆的對象)作為標(biāo)識
首先來看市場部的Marketing類:
Marketing.php
02 | include_once ( 'IAcmePrototype.php' ); |
03 | class Marketing extends IAcmePrototype |
05 | const UNIT = "Marketing" ; |
06 | private $sales = "sales" ; |
07 | private $promotion = "promotion" ; |
08 | private $strategic = "strategic planning" ; |
09 | public function setDept( $orgCode ) |
14 | $this ->department = $this ->sales; |
17 | $this ->department = $this ->promotion; |
20 | $this ->department = $this ->strategic; |
23 | $this ->department = "未識別的市場部" ; |
26 | public function getDept() |
28 | return $this ->department; |
30 | public function __clone() {} |
setDept()
方法的實現(xiàn)使用了一個參數(shù).并不是直接輸入市場部的部門,這個方法的參數(shù)是一個數(shù)字碼, 通過一個switch語句,限制了3種可接受的情況和默認情況,別外兩個原型實現(xiàn)也類似
Management.php
02 | include_once ( 'IAcmePrototype.php' ); |
03 | class Management extends IAcmePrototype |
05 | const UNIT = "Management" ; |
06 | private $research = "research" ; |
07 | private $plan = "planning" ; |
08 | private $operations = "operations" ; |
09 | public function setDept( $orgCode ) |
14 | $this ->department = $this ->research; |
17 | $this ->department = $this ->plan; |
20 | $this ->department = $this ->operations; |
23 | $this ->department = "未識別的管理部" ; |
26 | public function getDept() |
28 | return $this ->department; |
30 | public function __clone() {} |
Engineering.php
02 | include_once ( 'IAcmePrototype.php' ); |
03 | class Engineering extends IAcmePrototype |
05 | const UNIT = "Engineering" ; |
06 | private $development = "development" ; |
07 | private $design = "design" ; |
08 | private $sysAd = "system administration" ; |
09 | public function setDept( $orgCode ) |
14 | $this ->department = $this ->development; |
17 | $this ->department = $this ->design; |
20 | $this ->department = $this ->sysAd; |
23 | $this ->department = "未識別的工程部" ; |
26 | public function getDept() |
28 | return $this ->department; |
30 | public function __clone() {} |
以上這3個具體原型實現(xiàn)分別有其特定用途,不過它們都符合接口,可以創(chuàng)建各個原型實現(xiàn)的一個實例, 然后根據(jù)需要克隆多個實例.這個克隆的工作由Client類完成
客戶
客戶的設(shè)置非常簡單: 分別創(chuàng)建各個具體原型的一個實例, 然后按以下列表來克隆各個實例:
市場部門實例:
-----市場部門克隆
-----市場部門克隆
管理部門實例
-----管理部門克隆
工程部門實例
-----工程部門克隆
-----工程部門克隆
將來只使用這些克隆對象.使用獲取方法和設(shè)置方法將各個特定情況的信息賦給這些克隆對象.以下是Client的實現(xiàn)
Client.php
02 | function __autoload( $class_name ) |
04 | include_once $class_name . '.php' ; |
11 | public function __construct() |
13 | $this ->makeConProto(); |
14 | $Tess = clone $this ->market; |
15 | $this ->setEmployee( $Tess , "Tess Smith" , 101, 'ts101-1234' , 'tess.png' ); |
16 | $this ->showEmployee( $Tess ); |
17 | $Jacob = clone $this ->market; |
18 | $this ->setEmployee( $Jacob , "Jacob Jones" , 101, 'jj101-2234' , 'jacob.png' ); |
19 | $this ->showEmployee( $Jacob ); |
20 | $Ricky = clone $this ->manage; |
21 | $this ->setEmployee( $Ricky , "Ricky Rodriguez" , 203, 'rr101-5634' , 'ricky.png' ); |
22 | $this ->showEmployee( $Ricky ); |
23 | $Olivaia = clone $this ->engineer; |
24 | $this ->setEmployee( $Olivaia , "Olivaia Perez" , 302, 'op301-1278' , 'olivia.png' ); |
25 | $this ->showEmployee( $Olivaia ); |
26 | $John = clone $this ->engineer; |
27 | $this ->setEmployee( $John , "John Jacson" , 101, 'jj301-14548' , 'john.png' ); |
28 | $this ->showEmployee( $John ); |
30 | private function makeConProto() |
32 | $this ->market = new Marketing(); |
33 | $this ->manage = new Management(); |
34 | $this ->engineer = new Engineering(); |
36 | private function showEmployee(IAcmePrototype $employeeNow ) |
38 | $px = $employeeNow ->getPic(); |
39 | echo "<img src=$px width='150' height='150' /><br />" ; |
40 | echo $employeeNow ->getName(). '<br />' ; |
41 | echo $employeeNow ->getDept(). ':' . $employeeNow ::UNIT. '<br />' ; |
42 | echo $employeeNow ->getId(). '<hr />' ; |
44 | private function setEmployee(IAcmePrototype $employeeNow , $nm , $dp , $id , $px ) |
46 | $employeeNow ->setName( $nm ); |
47 | $employeeNow ->setDept( $dp ); |
48 | $employeeNow ->setId( $id ); |
49 | $employeeNow ->setPic( $px ); |
52 | $worker = new Client(); |
解釋:
客戶Client的構(gòu)造函數(shù)類包含3個私有屬性, 用來分別實例化3個具體原型類. makeConPro()
方法生成必要的實例.
接下來,使用克隆技術(shù)創(chuàng)建一個"員工"實例.然后,這個實例向一個設(shè)置方法setEmployee()發(fā)送特定的實例信息,這個設(shè)置方法使用IAcmePrototype接口類型提示,不過需要說明, 它只對第一個參數(shù)使用類型提示,其他參數(shù)都沒有類型提示, 并不要求它們派生自IAcmePrototype接口.克隆"員工"可以使用IAcmePrototype抽象類的所有設(shè)置方法以及具體原型類實現(xiàn)的setDept()方法.
要使用各個員工的數(shù)據(jù),Client類可以使用繼承的獲取方法.以下是運行Client輸出的結(jié)果
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
可以根據(jù)需要增加更多的克隆, 而且只需要對具體原型類完成一次實例化.使用原型模式時, 并不是建立具體類的多個實例,而只需要一個類實例化和多個克隆.
完成修改,增加特性
要記住,最重要(可能也是最基本)的是, 設(shè)計模式允許開發(fā)人員修改和增補程序,而不必一切從頭開始.例如, 假設(shè)總裁決定公司增加一個新的部門,比如研究部門(Research), 這會很難嗎?一點也不難.Research可以擴展IAcmePrototype抽象類, 然后實現(xiàn)抽象獲取方法和設(shè)置方法來反映這個研究部門的組織.需要說明,Client類中獲取方法和設(shè)置方法使用的代碼提示指示一個接口,而不是一個抽象類的具體實現(xiàn).所以, 只要增加的單元正確地實現(xiàn)了這個接口,就能順利地增加到應(yīng)用中, 而不會帶來波動,也不需要對程序中的其他參與者進行重構(gòu).
不僅可以增加更多的具體類, 還可以很容易地對各個類進行修改, 而不會造成破壞.例如假設(shè)這個組織的市場部決定,除了現(xiàn)有的部門外, 他們還需要一個特殊的在線市場部,. 這樣一來, switch/case操作需要一個新的分支(case), 還要有一個新的私有屬性(變量)來描述新增的這個部門.這個改變將封凍在單獨的類中, 而不會影響其他參與者.由于這種改變不會帶來破壞, 所以應(yīng)用的規(guī)模越大, 這一點就越重要.可以看到原型設(shè)計模式不僅支持一致性, 還支持靈活的改變.
PHP世界中的原型
由于PHP是一個服務(wù)器端語言,也是與MySQL數(shù)據(jù)庫交互的一個重要工具,所以原型設(shè)計模式尤其適用 .并不是為數(shù)據(jù)庫的第一個元素分別創(chuàng)建對象,PHP可以使用原型模式創(chuàng)建具體類的一個實例,然后利用數(shù)據(jù)庫中的數(shù)據(jù)克隆其余的實例(記錄).
了解克隆過程之后,與直接實例化的過程相比,你可能會問:"這有什么區(qū)別?" 換句話說,為什么克隆比直接實例化對象需要的資源少?它們的區(qū)別并不能直接看到. 一個對象通過克隆創(chuàng)建實例時, 它不會啟動構(gòu)造函數(shù).克隆能得到原始類的所有屬性, 甚至還包含父接口的屬性,另外還繼承了傳遞實例化對象的所有值.構(gòu)造函數(shù)生成的所有值以及存儲在對象屬性中的值都會成為克隆對象的一部分.所以沒有返回構(gòu)造函數(shù).如果發(fā)現(xiàn)你的克隆對象確實需要訪問構(gòu)造函數(shù)生成的值但又無法訪問, 這說明需要對類進行重構(gòu),使實例能擁有它們需要的一切信息, 而且可以把這些數(shù)據(jù)傳遞給克隆對象.
總的來說, 原型模式在很多不同類型的PHP項目中都很適用, 如果解決一個問題需要乃至創(chuàng)建型模式, 都可以使用原型模式.
希望本文所述對大家PHP程序設(shè)計有所幫助。