1. 程式人生 > >PHP面向物件_物件克隆clone和魔術方法__clone()

PHP面向物件_物件克隆clone和魔術方法__clone()

1.物件克隆 clone

  PHP4面向物件功能一個很大的缺點,是將物件視為另一種資料型別,這使得很多常見的OOP方法無法使用,如設計模式。這些方法依賴於將物件作為引用傳遞給其他類方法,而不是作為值傳遞,而按值傳遞卻是PHP的預設做法。幸好,PHP5解決了這個問題,現在所有物件在預設情況下都被視為引用。但是,由於所有物件都被視為引用而不是值,所以現在複製物件更為困難。如果嘗試複製一個引用的物件,這隻會指向原物件的地址位置。為了解決複製問題,PHP提供了一種克隆clone(關鍵字,不是方法)物件的顯式方法。

  可以在物件前面加clone關鍵字來克隆物件,如下:

destinationObject 
= clone targetObject;

克隆物件:

class Person{
    var $name;
    var $sex;
    var $age;

    function __construct($name, $sex, $age){
        $this->name = $name;
        $this->sex = $sex;
        $this->age = $age;   
    }

    function say(){
        echo "我的名字:" . $this->name . ",性別:"
. $this->sex . ",年齡:" .$this->age . "<br />"; } } $person1 = new Person("張三三", "男", 23); $person2 = clone $person1; //使用clone關鍵字克隆/複製物件,建立一個物件的副本 $person3 = $person1; //這不是複製物件,而是為物件多複製出一個訪問該物件的引用 $person1->say(); //呼叫原物件中的說話方式,列印原物件中的全部屬性值 $person2->say(); //呼叫副本物件中的說話方式,列印克隆物件中的全部屬性值
$person3->say(); //呼叫原物件中的說話方式,列印原物件中的全部屬性值

2.魔術方法__clone()

  在上面的程式中一共建立了兩個物件,其中有一個物件是通過clone關鍵字克隆出來的副本。兩個物件完全能獨立,但他們中的成員及屬性的值完全一樣。如果需要對克隆後的副本物件在克隆時重新為成員屬性賦初值,則可以在類中宣告一個魔術方法“__clone()”。該方法是在物件克隆時自動呼叫的,所以就可以通過此方法對克隆後的副本重新初始化。__clone()方法不需要任何引數。將上例中的程式碼改寫一下,在類中新增魔術方法__clone(),為副本物件中的成員屬性重新初始化。

class Person{
    var $name;
    var $sex;
    var $age;

    function __construct($name, $sex, $age){
        $this->name = $name;
        $this->sex = $sex;
        $this->age = $age;  
    }

    function say(){
        echo "我的名字:" . $this->name . ",性別:" . $this->sex . ",年齡:" .$this->age . "<br />";    
    }

    function __clone(){
        $this->name = "李四四";   //為副本物件中的name屬性重新賦值
        $this->age = 10;   //為副本物件中的age屬性重新賦值
    }
}

$person1 = new Person("張三三", "男", 23);
$person2 = clone $person1;  //建立一個物件的副本,並自動呼叫類中的__clone()方法

$person1->say();   //呼叫原物件中的說話方式,列印原物件中的全部屬性值
$person2->say();   //呼叫副本物件中的說話方式,列印克隆物件中的全部屬性值

執行結果:

我的名字:張三三,性別:男,年齡:23
我的名字:李四四,性別:男,年齡:10

3.單例類的加強:禁止克隆

  對於一個類的物件,如果使用“clone運算子”,就會複製出一個和當前物件完全一樣的新物件出來,並且,此時還會自動呼叫該類的魔術方法:__clone()(只要該類中有該方法)。
  則要實現單例類,就應該對這個單例類的物件“禁止克隆”。在PHP中,為防止對單例類物件的克隆來打破單例類的上述實現形式,通常還為其提供一個空的私有 (private修飾的)__clone()方法。

首先來看“未做禁止克隆”的效果:

class SingetonBasic {
    private static $instance;  //靜態變數要私有化,防止類外修改

    private function __construct() {    //建構函式私有化,類外不能直接新建物件

}

//private function __clone() {} //在__clone()前用private修飾,用來禁止克隆

public static function getInstance() {  //公共的靜態方法,public——外部的介面,static——不使用物件而是通過類名訪問
    if (!(self::$instance instanceof self)) { //私有靜態變數$instance為空
        self::$instance = new self();  //新建為自身的物件,並賦值給私有變數$instance
    }
    return self::$instance;	//返回私有變數$instance
}

}

$a = SingetonBasic::getInstance();
$b = SingetonBasic::getInstance();
var_dump($a === $b);   //結果為:boolean true     a和b指向的是同一個物件

$c = clone $a;
var_dump($a === $c); //結果為:boolean false      a和c指向的不是同一個物件

行結果為

boolean true

boolean false

  我們“作禁止克隆”處理,即把上面程式碼中的

private function __clone() {}   //在__clone()前用private修飾,用來禁止克隆

  這行程式碼去掉註釋。

執行結果為

boolean true

Fatal error: Call to private SingetonBasic::__clone()

  也就是,在克隆的時候,自動呼叫了__clone(),但是該方法被private修飾,不能再類的外部直接呼叫,結果報錯。