PHP 單例模式的實現

單例設計模式爲了解決在一個應用中只有一個實例【比如數據庫實例】,並且禁止clone 複製


在PHP中可以繼承單例模式來使用單例模式的特性,避免每次創建一個類都要創建一個對象

一般Sigleton類的實現 參考【https://stackoverflow.com/questions/3126130/extending-singletons-in-php

但是上面參考鏈接其實也有一點問題 【然後我自己添加了一個回答:) 看到的點個贊奧】


一般單例模式的實現

class Singleton
{
    private static $instance;
    private function __construct() {}
    final protected function __clone() {} #不允許被重寫並且會被子類繼承

    public static function getInstance()
    {
        if (! self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

// $s = new Singleton();#Fatal error: Call to private Singleton::__construct() 
$s1 = Singleton::getInstance();
$s2 = Singleton::getInstance();
var_dump($s1,$s2); #object(Singleton)#1 (0) { } object(Singleton)#1 (0) { }
#測試出來兩個實例是同一個對象

#測試clone 對象,會報錯,說明真的只有一個對象存在於應用中
// $s2 = clone $s1; #Fatal error: Call to protected Singleton::__clone() 


上面的代碼看上去沒有任何問題但是如果我們想讓單例模式可以被繼承,讓子類也具有單例模式的特性,就會出錯

Java 中構造函數是私有的不能被繼承,默認情況下Java的子類會在構造函數中調用父類的無參數構造方法

PHP 可以繼承,哪怕父類是私有構造器


下面是測試 PHP代碼

class A extends Singleton
{

}

$a = new A();#Fatal error: Call to private Singleton::__construct() 調用了父類的private 的構造函數
#如果子類中沒有構造函數就使用父類的構造方法,如果有自己的構造方法,就不會自動調用父類的構造方法,需要顯式調用 parent::__construct();
#所以還是需要調用 靜態方法獲得實例


$a = A::getInstance();
var_dump($a);#object(Singleton)#1 (0) { } #出毛病了,實例化之後還是Singleton對象

#解決方法使用PHP動態綁定,關鍵字static 除了靜態方法之外下面羅列動態綁定的特性 

參考http://php.net/manual/zh/language.oop5.late-static-bindings.php
有三個特性 

1) 在非靜態環境下,所調用的類即爲該對象實例所屬的類【就代表了這個實例】

2) 由於 $this-> 會在同一作用範圍內嘗試調用私有方法,而 static:: 則可能給出不同結果。 [$this 可以調用同一範圍的私有變量方法 static 相當於類名 比如說 A::$instance,就不行]

如果 static::function 或者static::$instance 調用的是同一個類裏面的方法不管是不是private 都ok 但是如果是不同類的 就會報錯

猜測性小結: 只要static 調用的元素不在同一個類裏面 private 就會報錯

3) 另一個區別是 static:: 只能用於靜態屬性 不是方法【方法都可以調用】。


所以最後單例模式可以寫成如下格式


需要注意的 使用 self:: 或者 __CLASS__ 對當前類的靜態引用,取決於定義當前方法所在的類


#定義一個抽象類 被其他類繼承
abstract class Singleton
{
    protected static $instance; // 這裏必須是protected 如果要讓子類繼承 其次protected才能被 static 調用
    private function __construct(){}
    final protected function __clone(){}
    
    public static function getInstance()
    {
        if (! static::$instance instanceof static) {
            static::$instance = new static();
        }
        return static::$instance;
    }
}

class A extends Singleton
{
   protected static $instance; #必須重定義 否則就是父類的$instance
}

//如果子類裏面重新定義了構造函數 自然它就算不上單例模式
class B extends A
{
    protected static $instance;
}

$a = A::getInstance();
$b = B::getInstance();
$c = B::getInstance();
$d = A::getInstance();
$e = A::getInstance();

var_dump($a,$b,$c,$d,$e);

#object(A)#1 (0) { }
#object(B)#2 (0) { } 
#object(B)#2 (0) { } 
#object(A)#1 (0) { } 
#object(A)#1 (0) { }


小結:

public static function getInstance()
{
    if (! static::$instance instanceof static) {
            static::$instance = new static();
    }
    return static::$instance;
}
# 上面方法中必須使用static::$instance 不能使用self::$instance 的原因是子類動態調用的是子類的靜態屬性
# 靜態屬性必須是 protected 原因是 動態調用關鍵字static 調動不是在一個原生類裏面的private的屬性時相當於直接  類名::$instance
# 子類必須重定義 protected static $instance; 否則使用的是父類的靜態屬性
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章