php實現(xiàn)單例模式最安全的做法

字號:


    作為一種常用的設計模式,單例模式被廣泛的使用。那么如何設計一個單例才是最好的呢?
    通常我們會這么寫,網(wǎng)上能搜到的例子也大部分是這樣:
    代碼如下:
    class a
    {
        protected static $_instance = null;
        protected function __construct()
        {
            //disallow new instance
        }
        protected function __clone(){
            //disallow clone
        }
        public function getinstance()
        {
            if (self::$_instance === null) {
                self::$_instance = new self();
            }
            return self::$_instance;
        }
    }
    class b extends a
    {
        protected static $_instance = null;
    }
    $a = a::getinstance();
    $b = b::getinstance();
    var_dump($a === $b);
    將__construct方法設為私有,可以保證這個類不被其他人實例化。但這種寫法一個顯而易見的問題是:代碼不能復用。比如我們在一個一個類繼承a:
    代碼如下:
    class b extends a
    {
        protected static $_instance = null;
    }
    $a = a::getinstance();
    $b = b::getinstance();
    var_dump($a === $b);
    上面的代碼會輸出:
    代碼如下:
    bool(true)
    問題出在self上,self的引用是在類被定義時就決定的,也就是說,繼承了b的a,他的self引用仍然指向a。為了解決這個問題,在php 5.3中引入了后期靜態(tài)綁定的特性。簡單說是通過static關鍵字來訪問靜態(tài)的方法或者變量,與self不同,static的引用是由運行時決定。于是簡單改寫一下我們的代碼,讓單例模式可以復用。
    代碼如下:
    class c
    {
        protected static $_instance = null;
        protected function __construct()
        {
        }
        protected function __clone()
        {
            //disallow clone
        }
        public function getinstance()
        {
            if (static::$_instance === null) {
                static::$_instance = new static;
            }
            return static::$_instance;
        } 
    }
    class d extends c
    {
        protected static $_instance = null;
    }
    $c = c::getinstance();
    $d = d::getinstance();
    var_dump($c === $d);
    以上代碼輸出:
    代碼如下:
    bool(false)
    這樣,簡單的繼承并重新初始化$_instance變量就能實現(xiàn)單例模式。注意上面的方法只有在php 5.3中才能使用,對于之前版本的php,還是老老實實為每個單例類寫一個getinstance()方法吧。
    需要提醒的是,php中單例模式雖然沒有像java一樣的線程安全問題,但是對于有狀態(tài)的類,還是要小心的使用單例模式。單例模式的類會伴隨php運行的整個生命周期,對于內(nèi)存也是一種開銷。