苦逼软件工程师,你还在这样写单例吗
昨天看到一个同学写了一个访问数据的单例程序,先给大家看看他写的代码:
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 ;
}
} |