1. 程式人生 > >苦逼程式設計師,你還在這樣寫單例嗎

苦逼程式設計師,你還在這樣寫單例嗎

    昨天看到一個同學寫了一個訪問資料的單例程式,先給大家看看他寫的程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <?php class MYSQL_DEMO {//這個類用來模擬MYSQLI類 public function close() { echo "MYSQL_DEMO::close()"; }

}

//DB類是用於產生單例

class DB { private static $dao
;
public static function getInstance() { if(!self::$dao) { self::$dao = new MYSQL_DEMO(); } return self::$dao; } } class A { private static $dao; public function __construct() { self::$dao = DB::getInstance();    } } class extends A{ } class C extends A { } $tmp = new B(); $tmp = new
C();

我們說折斷程式碼是有問題的,您知道問題出自那嗎?

我們分析一下這個程式,MYSQL_DEMO就是mysqli類,DB類是資料鏈接類,主要目的就是建立資料庫連結,DB類有一個靜態變數$dao用來記錄MYSQL_DEMO物件,並且是DB::getIntance()的返回值,也許您還沒有意識到錯誤,因為僅僅這樣寫的話程式是能夠正常執行的。

但是就在昨天,這個同學想加上mysql的close函式用於及時釋放記憶體,於是程式碼就變成了下面的樣子

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <?php class MYSQL_DEMO { public function close() { echo "MYSQL_DEMO::close()"; } } class DAO { private static $dao; public static function getInstance() { if(!self::$dao) { self::$dao = new MYSQL_DEMO(); } return self::$dao; } } class A { private static $dao; public function __construct() { self::$dao = DAO::getInstance();    } public function __destruct() { //新加語句 self::$dao->close();//新加語句 self::$dao = null;//新加語句 }//新加語句 } class extends A{ } class C extends A { } $tmp = new B(); $tmp = new C();

此時看上去也沒有問題,但是當我們執行程式的時候發現,程式報錯了,錯誤如下:

MYSQL_DEMO::close()
Fatal error: Call to a member function close() on a non-object in /home/work/Webroot/index.php on line 28


讀者可以自己分析一下錯誤的原因。。。

錯誤分析如下:

首先 $tmp = new B();  B的父類A屬性dao對應的值為mysql_demo物件

我們在看一下$tmp = new C();這句話分兩步執行

1。 new C();這個時候也沒有問題,此時的C的父類A屬性dao對應的值仍然為B父類A屬性對應的mysql_demo

2。 $tmp = new C();對$tmp賦值,這個時候就有問題了,賦值完成後B類就沒有任何引用了,那麼就會掉用B類的解構函式,這個過程中也會掉用A類的解構函式,掉用完成後B的父類A屬性dao對應的mysql_demo物件就沒有了。也就是說C父類A屬性dao對應的mysql_demo就沒有了

當整個指令碼執行完成後, 會掉用C的解構函式,相應的需要掉用A的解構函式,但此時A的解構函式中執行了self::$dao->close();

上面我們分析到其實self::$dao這個變數指向的物件已經被銷燬了。如果再次掉用close函式肯定會報錯嘮

所有這些都是因為單例沒有寫對

正確的單例寫法如下:

1 2 3 4 5 6 7 8 9 10 11 class A {    public static function getInstance()    {    if(! (self::$_instance instanceof self) )   {    self::$_instance = new self();    //請注意,這應應該是self return self::$_instance;    }   }