1. 程式人生 > >淺談設計模式之原型模式

淺談設計模式之原型模式

背景知識:

  1. 我們常說的設計模式其實是一種程式碼規範,遵從設計模式所編寫的程式碼並不是最高效的,但是是可維護的。
  2. 設計模式主要有三類:建立型設計模式,結構型設計模式以及行為型設計模式。
  3. 設計模式遵循的幾個原則:開閉原則,里氏代換原則,依賴倒轉原則,單一職責原則,合成複用原則,介面隔離原則,最小知識原則。

本文主要講述原型模式,原型模式和我們在上一篇中講的單例模式相似,都是關於物件的。只是單例模式的物件只建立一次,之後所有的操作都是圍繞這一個物件展開的。而原型模式的目的則是為了獲得新的物件,通過克隆的方式減少不斷例項化所帶來的開銷。

在附上原型模式的事例程式碼之前,我們先來聊聊php中的深淺拷貝

:我們都知道將一個物件賦值給一個變數是引用賦值(淺拷貝),將一個普通值賦值給一個變數是傳值賦值(深拷貝),具體的效果是這樣子的:

//傳值賦值
$a=1;
var_dump($a);   ---> 列印結果為1
$b=$a;
$b=2;
var_dump($a);   ---> 列印結果仍為1

//引用賦值
class Dog{
    public $name;
    public function __construct($name){
        $this->name=$name;
    }
}
$a=new Dog(1);
var_dump($a->name);   ---> 列印結果為1
$b=$a;
$b->name=2;
var_dump($a->name);   ---> 列印結果為2

從以上的結果中我們不難看出,物件之間的賦值預設是引用賦值(淺拷貝),PHP提供了一個clone函式,可以實現物件之間的傳值賦值(深拷貝):

//傳值賦值
class Dog{
    public $name;
    public function __construct($name){
        $this->name=$name;
    }
}
$a=new Dog(1);
var_dump($a->name);   ---> 列印結果為1
$b=clone $a;
$b->name=2;
var_dump($a->name);   ---> 列印結果變為1

但是clone函式有一個缺點就是原物件的普通屬性可以傳值賦值,但是原物件的物件屬性仍然是引用賦值:

class Cat{
    public $name;
    public function __construct($name){
        $this->name=$name;
    }
}

class Dog{
    public $name;
    public $cat;
    public function __construct($name){
        $this->name=$name;
        $this->cat=new Cat('cat');
    }
}


$a=new Dog('dog');
var_dump($a->cat->name);  ---> 列印結果是 cat
$b=clone $a;
$b->cat->name='catcat';
var_dump($a->cat->name);  ---> 列印結果是 catcat

如何解決上述的問題呢?有兩種方式:一種是重寫clone函式,一種是序列化之後賦值:

//方式一:重寫clone函式
class Cat{
    public $name;
    public function __construct($name){
        $this->name=$name;
    }
}

class Dog{
    public $name;
    public $cat;
    public function __construct($name){
        $this->name=$name;
        $this->cat=new Cat('cat');
    }
    public function __clone(){
        $this->cat=clone $this->cat;
    }
}

$a=new Dog('dog');
var_dump($a->cat->name);  ---> 列印結果是 cat
$b=clone $a;
$b->cat->name='catcat';
var_dump($a->cat->name);  ---> 列印結果仍為 cat


//方式二:序列化之後賦值
class Cat{
    public $name;
    public function __construct($name){
        $this->name=$name;
    }
}

class Dog{
    public $name;
    public $cat;
    public function __construct($name){
        $this->name=$name;
        $this->cat=new Cat('cat');
    }
}

$a=new Dog('dog');
var_dump($a->cat->name);  ---> 列印結果是 cat
$b=serialize($a);
$b=unserialize($b);
$b->cat->name='catcat';
var_dump($a->cat->name);  ---> 列印結果仍為 cat

我們理清楚深淺複製之後,我們來看看如何使用原型模式建立物件:

abstract class Prototype{
	abstract public function __clone();
}

class BasketBall {
	public $name;
}

class Person extends Prototype{
	public $name;
	public $age;
	//物件屬性
	public $hobby;
	public function __construct($attributes=array()){
		foreach($attributes as $key=>$attr){
			if(property_exists($this,$key)){
				$this->$key=$attr;
			}
		}
	}
	public function __clone(){
		$this->hobby = clone $this->hobby;
	}
}
//建立原型物件
$person = new Person(array('name'=>'example','age'=>0,'hobby'=>new Basketball()));
//需要物件的時候直接clone就好,不需要再例項化
$yaoming= clone $person;

以上就是原型模式的使用方法。