設計模式實踐--單例模式

zhaohehe發表於2017-01-19

介紹

數學與邏輯學中,singleton定義為“有且僅有一個元素的集合”。
單例模式是設計模式中最簡單的形式之一。這一模式的目的是使得類的一個物件成為系統中的唯一例項。

一個典型的單例模式在php中的實現如下:

class test 
{
    private static $_instance;    //儲存類例項的私有靜態成員變數

    //定義一個私有的建構函式,確保單例類不能通過new關鍵字例項化,只能被其自身例項化
    private final function __construct() 
    {
        echo 'test __construct';
    }

    //定義私有的__clone()方法,確保單例類不能被複制或克隆
    private function __clone() {}

    //對外提供獲取唯一例項的方法
    public static function getInstance() 
    {
        //檢測類是否被例項化
        if ( ! (self::$_instance instanceof self) ) {
            self::$_instance = new test();
        }
        return self::$_instance;
    }
}

test::getInstance();

類自身只提供一個對外的getInstance()方法來獲取例項,這裡的關鍵在於用了一個靜態變數$_instance來儲存例項,靜態變數是屬於類的,在php的一次生命週期中,它會一直存在,只要有人需要拿到這個類的物件,getInstance() 就會去把唯一的$_instance返回回來,實現了物件的唯一性。

在容器中實現單例

ioc容器簡介

Sakura框架是基於一個叫做container的東西的,這個container是一個類,它有一個叫做$bindings的陣列,這個陣列中存放的是被bind上去一對鍵值,key是一個字串,用來標示,value可能是類的名字,或者一個匿名函式,總之它會告訴container當有人想從容器中根據key來取出一個物件的時候,該如何去製造出這個物件。這個概念是和laravel一致的。

容器中的單例

容器中的單例模式和上述的典型單例模式最大的區別就是,典型的單例模式中類自身會保證自己只被例項化一次。但是由於在container中,所有物件都是通過container來例項化的,所以自然,保證物件的唯一性這個職責也要交給container。
在容器中,普通物件的繫結通過bind()方法,示例如下:

$app = new System\Foundation\Container();

$app->bind('test', \System\test::class);

而要繫結一個單例物件的時候則用下面的方式:

$app->singleton('test', \System\test::class);

在容器的實現中,singleton()方法的程式碼:

public function singleton($abstract, $concrete = null)
{
    $this->bind($abstract, $concrete, true);
}

public function bind($abstract, $concrete = null, $shared = false)
{
    if ( ! $concrete instanceof Closure ) {
        $concrete = $this->getClosure($abstract, $concrete);
    }
    $this->bindings[$abstract] = compact('concrete', 'shared');
}

可以看到,唯一的區別就是把$this->bindings[$abstract]的shared設定為了true。在使用make方法來取出物件的時候,容器會根據當前$abstract對應的shared屬性來判斷是否需要單例它。
在container的make()方法中,用瞭如下程式碼:

if ($this->isShared($abstract)) {
    $this->instances[$abstract] = $object;
}

isShared()方法來判斷是否為單例,如果是,則把該物件寫入到容器的$this->instances[]中,這樣當下次呼叫容器的make()方法來再次取出該物件的時候,先用如下程式碼進行判斷,$this->instances[]中是否已經儲存有了這個物件,如果有,直接返回該物件,這樣就保證了物件的唯一性。

if (isset($this->instances[$abstract])) {
    return $this->instances[$abstract];
}

完整的程式碼請看

https://github.com/zhaohehe/Sakura/blob/ma...

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章