回覆列表
  • 1 # 笑看江湖ol

    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 除了靜態方法之外下面羅列動態繫結的特性

    有三個特性 

    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

    $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; 否則使用的是父類的靜態屬性。

  • 中秋節和大豐收的關聯?
  • 赤壁賦斷句全文?