苦逼程式設計師,你還在這樣寫單例嗎
昨天看到一個同學寫了一個訪問資料的單例程式,先給大家看看他寫的程式碼:
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()" ;
}
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 B 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 B 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 ;
}
}
|