天天看點

php中原型模式(Prototype Pattern)

概念

原型模式(Prototype Pattern):與工廠模式類似,都是用來建立對象的。利用克隆來生成一個大對象,減少建立時的初始化等操作占用開銷

場景

多用于建立大對象,或初始化繁瑣的對象。如遊戲中的背景,地圖。web中的畫布等等

1,有些時候,我們需要建立多個類似的大對象。如果直接通過new對象,開銷很大,而且new完還得進行重複的初始化工作。可能把初始化工作封裝起來的,但是對于系統來說,你封不封裝,初始化工作還是要執行。,

2,原型模式則不同,原型模式是先建立好一個原型對象,然後通過clone這個原型對象來建立新的對象,這樣就免去了重複的初始化工作,系統僅需記憶體拷貝即可。

結構圖

php中原型模式(Prototype Pattern)

示例

如果說,我們現在正開發一個遊戲,有不同的地圖,地圖大小都是一樣的,并且都有海洋,但是不同的地圖溫度不一樣。

<?php

//抽象原型類
abstract class Prototype
{
    abstract function __clone();
}

//具體原型類
class Map extends Prototype
{
    public $width;
    public $height;
    public $sea;


    public function setAttribute(array $attributes)
    {
        foreach ($attributes as $key => $val) {
            $this->$key = $val;
        }
    }

    public function __clone()
    {
    }
}

//海洋類.這裡就不具體實作了。
class Sea
{
}

//使用原型模式建立對象方法如下
//先建立一個原型對象
$map_prototype = new Map;
$attributes = array('width' => 40, 'height' => 60, 'sea' => (new Sea));
$map_prototype->setAttribute($attributes);
//現在已經建立好原型對象了。如果我們要建立一個新的map對象隻需要克隆一下
$new_map = clone $map_prototype;

var_dump($map_prototype);
var_dump($new_map);      

通過上面的代碼,我們可以發現利用原型模式,隻需要執行個體化并初始化一個地圖原型對象。以後生産一個地圖對象,都可以直接通過clone原型對象産生。省去了重新初始化的過程。

但是上面的代碼還是存在一些問題。那就是它隻是一個淺拷貝,什麼意思呢?map原型對象有一個屬性sea存放了一個sea對象,在調用setAttribute的時候,對象的指派方式預設是引用。而當我們克隆map對象時,直接克隆了map的sea屬性,這就使得克隆出來的對象與原型對象的sea屬性對指向了,同一個sea對象的記憶體空間。如果這個時候,我們改變了克隆對象的sea屬性,那麼原型對象的sea屬性也跟着改變。

這顯然是不合理的,我們想要的結果應該是深拷貝,也就是改變克隆對象的所有屬性,包括用來存放sea這種其他對象的屬性時,也不影響原型對象。

當然,講到這裡你可以當我在胡說。但我還是建議你列印下原型對象和克隆對象,看一下他們的sea屬性吧,然後去好好了解一下什麼叫深拷貝和淺拷貝。

深拷貝的實作

深拷貝的實作,其實也簡單,我們隻要實作Map類的克隆方法就行了。這就是我們為什麼要定義一個抽象原型類的原因。我們利用抽象類,強制所有繼承的具體原型類都必須來實作這個克隆方法。改進如下:

<?php

//具體原型類
class Map extends Prototype
{
    public $width;
    public $height;
    public $sea;

    public function setAttribute(array $attributes)
    {
        foreach ($attributes as $key => $val) {
            $this->$key = $val;
        }
    }

    //實作克隆方法,用來實作深拷貝
    public function __clone()
    {
        $this->sea = clone $this->sea;
    }
}      

繼續閱讀