PHP trait與單例模式 (一次編寫,到處使用)

摘要: PHP的單例模式是爲了避免重複創建對象帶來的資源消耗,應用非常廣泛,我們可以利用trait來編寫getInstance()代碼,需要用到的地方use trait即可. 達到一次編寫,到處使用 的效果

 

一  trait

  php是單繼承的語言,無法同時從兩個基類中繼承屬性和方法,爲了解決這個問題,php出了Trait這個特性.

個人理解的trait是: trait = abstract class - interface,其中擁有interface的多個繼承的性質.'繼承'的方式是 use trait;

 

 

二  單例模式

  單例模式確保類在全局只能有一個實例,因爲它的實例是由自己保存,在類的外部也無法對該類進行實例化.PHP的單例模式是爲了避免重複創建對象帶來的資源消耗.

這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。

實際項目中像數據庫查詢,日誌輸出,全局回調,統一校驗等模塊。這些模塊功能單一,但需要多次訪問,如果能夠全局唯一,多次複用會大大提升性能。

 

 

  • 單例類只能有一個實例
  • 單例類必須自己創建自己的唯一實例
  • 單例類必須給所有其他對象提供這一實例

 

三   trait與單例模式結合使用

  3.1 簡單demo

    

trait Singleton
{
    private static $instance;

    static function getInstance(...$args)
    {
        if(!isset(self::$instance)){
            self::$instance = new static(...$args);
        }
        return self::$instance;
    }
}

class MySingleton
{
    use Singleton;
}

$mySingleton = Mysingleton::getInstance();

 

  3.2 爲什麼不用 new self()

  new self() 和 new static 的區別: 只有在繼承中才能體現出來、如果沒有任何繼承、那麼二者沒有任何區別 然後 new self() 返回的實列是不會變的,無論誰去調用,都返回的一個類的實列, 而 new static則是由調用者決定的。

  此處如果用new self() ,那麼我們獲取到的對象就不是MySingleton了

 

 

  3.3 類,基類,trait之間同名函數的優先級

  先說結論,類 > trait > 基類,簡單實例:

 

 

<?php
trait Dog{
    public $name="dog";
    public function drive(){
        echo "This is dog drive";
    }
    public function eat(){
        echo "This is dog eat";
    }
}

class Animal{
    public function drive(){
        echo "This is animal drive";
    }
    public function eat(){
        echo "This is animal eat";
    }
}

class Cat extends Animal{
    use Dog;
    public function drive(){
        echo "This is cat drive";
    }
}
$cat = new Cat();
$cat->drive();
echo "<br/>";
$cat->eat();

?>

 

得出:

This is cat drive
This is dog eat

 

  3.4 特殊情況,基類use trait,本類不use

trait A{
    private static $instance;
    static function getInstance()
    {
        if(!isset(self::$instance)){
            self::$instance = new static();
        }
        return self::$instance;
    }
}

class B{
    use A;
    function a()
    {
        var_dump('call at B');
    }
}

class C extends B{
    function a()
    {
        var_dump('call at c');
        parent::a();
    }
}

class D extends B{
    use A;
    function a()
    {
        var_dump('call at D');
        parent::a(); 
    }
}
$b = B::getInstance();
$c = C::getInstance();
$d = D::getInstance();

$c->a();
$d->a();
得出:

string(9) "call at B"
string(9) "call at D"
string(9) "call at B"

 

  此時我們與疑問了,根據3.2所寫,調用者是 C::getInstance();  怎麼$c->a();像變成了 $b->a();一樣呢???
(個人觀點,也驗證了)其實這裏的$c,就是class B

  接下來進行驗證:

 

trait A{
    private static $instance;
    static function getInstance()
    {
        if(!isset(self::$instance)){
            self::$instance = new static();
        }
        return self::$instance;
    }
}

class B{
    use A;function h(){
        var_dump('h call at b');
    }

}

class C extends B{function h(){
        var_dump('h call at c');
    }
}$b = B::getInstance();
$c = C::getInstance();$c->h();

 

得出:

string(11) "h call at b" 

  而我們去掉 class c 中的 h(), 會沒有打印,驗證了 這裏的$c,就是class B 這句話.

  本篇就講這麼多,有興趣的可以更深入瞭解trait與單例模式的知識點.

 

 

 

 

 

發佈了16 篇原創文章 · 獲贊 0 · 訪問量 1909
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章