原型模式其實更形象的來說應該叫克隆模式,它主要的行為是對物件進行克隆,但是又把被克隆的物件稱之為最初的原型,于是,這個模式就這樣被命名了,說真的,從使用方式來看真的感覺叫克隆模式更貼切一些,
Gof類圖及解釋
GoF定義:用原型實體指定創建物件的種類,并且通過拷貝這些原型創建新的物件
GoF類圖

代碼實作
abstract class Prototype
{
public $v = 'clone' . PHP_EOL;
public function __construct()
{
echo 'create' . PHP_EOL;
}
abstract public function __clone();
}
首先我們通過模擬的方式定義了一個原型,這里主要是模擬了__clone()這個方法,其實這是PHP自帶的一個魔術方法,根本是不需要我們去進行定義的,只需要在原型類中進行實作就可以了,當外部使用clone關鍵字進行物件克隆時,直接就會進入這個魔術方法中,在這個魔術方法里面我們可以對屬性進行處理,特別是針對參考屬性進行一些獨特的處理,在這個例子中,我們只使用了一個值型別的變數,無法體現出參考型別的問題,我們將在后面的實體中演示對參考型別變數的處理,
class ConcretePrototype1 extends Prototype
{
public function __clone()
{
}
}
class ConcretePrototype2 extends Prototype
{
public function __clone()
{
}
}
模擬的具體實作的原型,其實就是主要去具體的實作__clone()方法,后面我們看具體的例子時再說明,
class Client
{
public function operation()
{
$p1 = new ConcretePrototype1();
$p2 = clone $p1;
echo $p1->v;
echo $p2->v;
}
}
$c = new Client();
$c->operation();
客戶端使用clone來復制$p1,可以看到$p2也具有相同的$v屬性,
- 原型模式看似就是復制了一個相同的物件,但是請注意,復制的時候,__construct()方法并沒有被呼叫,也就是當你運行這段代碼的時候,create只輸出了一次,這也就帶出了原型模式最大的一個特點——減少創建物件時的開銷,
- 基于上述特點,我們可以快速的復制大量相同的物件,比如要給一個陣列中塞入大量相同的物件時,
- 復制出來的物件中如果都是值型別的屬性,我們可以任意修改,不會對原型產生影響,而如果有參考型別的變數,則需要在__clone()方法進行一些處理,否則修改了復制物件的參考變數中的內容,會對原型物件中的內容有影響,
我們的手機作業系統(也可以想象一下PC電腦的作業系統),都是怎樣安裝到設備中呢?其實都是不停的復制拷貝最初的那一套系統,用微軟的例子非常好說明這個問題,當年微軟能夠成為一個帝國,其實也是因為他不停的將winodws作業系統拷貝復制到光碟中,然后賣給千家萬戶(當然,這里沒中國什么事兒),而中國市場呢,大量的高手破解了windows之后也是由這一份檔案不停的復制拷貝才裝到了我們的電腦中,手機、智能設備等各類產品的作業系統、軟體都是如此,一次開發無限拷貝正是軟體行業暴利的原因,畢竟我們的系統也是由不少的工程師日以繼夜的996在Android原生系統的基礎上開發出來的,趕緊不斷的復制到即將出廠的手機上吧!!
完整代碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/08.prototype/source/prototype.php
實體
同樣還是拿手機來說事兒,這次我們是根據不同的運營商需要去開發一批定制機,也就是套餐機,這批手機說實話都并沒有什么不同,大部分都是相同的配置,但是運營商系統不同,而且偶爾有一些型號的CPU和記憶體也可能存在不同,這個時候,我們就可以用原型模式來進行快速的復制并且只修改一部分不相同的地方啦,
原型模式生產手機類圖

完整原始碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/08.prototype/source/prototype-phone.php
<?php
interface ServiceProvicer
{
public function getSystem();
}
class ChinaMobile implements ServiceProvicer
{
public $system;
public function getSystem(){
return "中國移動" . $this->system;
}
}
class ChinaUnicom implements ServiceProvicer
{
public $system;
public function getSystem(){
return "中國聯通" . $this->system;
}
}
class Phone
{
public $service_province;
public $cpu;
public $rom;
}
class CMPhone extends Phone
{
function __clone()
{
// $this->service_province = new ChinaMobile();
}
}
class CUPhone extends Phone
{
function __clone()
{
$this->service_province = new ChinaUnicom();
}
}
$cmPhone = new CMPhone();
$cmPhone->cpu = "1.4G";
$cmPhone->rom = "64G";
$cmPhone->service_province = new ChinaMobile();
$cmPhone->service_province->system = 'TD-CDMA';
$cmPhone1 = clone $cmPhone;
$cmPhone1->service_province->system = 'TD-CDMA1';
var_dump($cmPhone);
var_dump($cmPhone1);
echo $cmPhone->service_province->getSystem();
echo $cmPhone1->service_province->getSystem();
$cuPhone = new CUPhone();
$cuPhone->cpu = "1.4G";
$cuPhone->rom = "64G";
$cuPhone->service_province = new ChinaUnicom();
$cuPhone->service_province->system = 'WCDMA';
$cuPhone1 = clone $cuPhone;
$cuPhone1->rom = "128G";
$cuPhone1->service_province->system = 'WCDMA1';
var_dump($cuPhone);
var_dump($cuPhone1);
echo $cuPhone->service_province->getSystem();
echo $cuPhone1->service_province->getSystem();
說明
- 列印了很多東西呀,不過主要的還是看看移動手機,也就是CMPhone中的__clone()方法,我們沒有重新去初始化一個新物件,這時,復制的$cmPhone1物件中的service_province和$cmPhone中的是同一個物件,沒錯,這就是參考的復制問題,參考只是復制了參考的地址,他們指向的是同一個物件,當$cmPhone1修改service_province物件里面的屬性內容時,$cmPhone里面的service_province物件里面的屬性也跟著改變了,
- 在CUPhone中,我們重新new了一個新的service_province物件,這次外面的$cuPhone1對該物件中的屬性修改時就不會影響$cuPhone中參考物件的值,
- 原型模式中最主要的就是要注意上述兩點,而普通的值屬性會直接進行復制,不會產生這個問題,這里又牽涉出另外兩個概念:淺復制和深復制
- 淺復制,是指被復制物件的所有變數都含有與原來物件相同的值,而所有的對其他物件的參考都仍然指向原來的物件
- 深復制把參考物件的變數指向復制過的新物件,而不是原有的被參考的物件
- 關于參考和值的問題,我們將在其他的文章中進行講解,請關注微信或掘金號
下期看點
原型模式雖然平常用得不多,但是學習之后發現還真是挺有用的,特別是需要大量的重復物件時,可以大大節約新建物件的資源需求,以后還是需要多多練習早日應用在實際的業務場景中,下一個又會是誰呢?別急別急,先去下個館子,廚師、服務員、顧客,這三個要素就能組成一個神奇的模式:命令模式
關注公眾號:【硬核專案經理】獲取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、專案管理學習資料
知乎、公眾號、抖音、頭條搜索【硬核專案經理】
B站ID:482780532
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/236921.html
標籤:PHP
上一篇:重寫Laravel例外處理類
