在面向对象编程中,设计模式是一种常用的解决特定问题的模板。其中,单例模式是一种确保一个类只有一个实例,并提供一个全局访问点的模式。在PHP中,自5.0版本以来,它已经是一个完全面向对象的语言,许多设计模式,包括单例模式,也被广泛应用于PHP及其众多的MVC框架中。本文将通过一步步的方法来实现PHP中的单例模式,并解决可能遇到的问题。
在PHP开发中(尤其是使用MVC框架时),经常需要包含/引入一个数据库类、上传类或Cookie类,如下所示:
<?php
require 'DB.class.php';
require 'Upload.class.php';
require 'Cookie.class.php';
// 但需要确保只有一个DB或Cookie类的实例,因为一个实例就足够了,更多实例将会引起问题。这时,单例模式就派上用场了。
单例模式简而言之,就是如果一个程序员已经实例化了一个特定类的实例,那么另一个程序员就不能实例化第二个。现在,让从头开始看看如何实现它。
创建一个名为Singleton的类,现在可以实例化任意数量的对象。这里可以使用Singleton类创建两个对象:
class Singleton {
}
$s1 = new Singleton();
$s2 = new Singleton();
// 那么$s1和$s2是同一个对象吗?当然不是,可以通过一个简单的if语句来判断:
注意:在判断两个对象是否为同一个对象时,必须使用“===”,而不是“==”。
如果想要阻止类用户创建他们想要的任意数量的Singleton类对象,该怎么做呢?可能认为对象是通过类的构造函数创建的。那么,为什么不把构造函数隐藏在类内部,这样外部就无法使用它,如下代码所示:
class Singleton2 {
protected function __construct() {
}
}
$s3 = new Singleton2();
// 致命错误:从无效的上下文中调用受保护的Singleton2::__construct()
现在可以看到,把构造函数设为受保护的,但新问题是:没有人可以再创建对象了。
为了让类用户能够创建对象,需要留下一个对象创建的“接口”,所以添加了一个名为getIns()的新方法,它是public(对外开放)和static(可以类名调用)的:
class Singleton3 {
public static function getIns() {
return new self();
}
protected function __construct() {
}
}
// 只是在类内部创建了一个类本身的实例,然后返回它。现在再次测试:
结果仍然是:s4和s5不是同一个对象。这两个对象仍然不是同一个对象,为什么?因为Singleton3::getIns()被调用了两次,因此创建了两个对象。但现在,控制权在手中,可以改进getIns()以实现想要的结果。
现在可以在getIns()中添加一些检查:
class Singleton4 {
protected static $ins = NULL;
public static function getIns() {
if (self::$ins === null) {
self::$ins = new self();
}
return self::$ins;
}
protected function __construct() {
}
}
// 现在在类的一个受保护属性$ins中存储类的实例。然后当getIns()被调用时,在这里进行一些检查:如果$ins是NULL,那么实例化一个类对象。最后,返回self::$ins。
让测试一下:
$s6 = Singleton4::getIns();
$s7 = Singleton4::getIns();
if ($s6 === $s7) {
echo 's6和s7是同一个对象';
} else {
echo 's6和s7不是同一个对象';
}
// 输出:s6和s7是同一个对象
现在得到了想要的结果!现在类只有一个实例了。但这还不够,如果有另一个类叫做Multi,它继承了原始类:
现在需要在父构造函数前加上final关键字:
class Singleton5 {
protected static $ins = NULL;
public static function getIns() {
if (self::$ins === null) {
self::$ins = new self();
}
return self::$ins;
}
// 添加final,所以这个方法不能被覆盖!
final protected function __construct() {
}
}
// 现在如果再次运行脚本:
输出:Fatal error: Cannot override final method Singleton5::__construct()。
class Singleton6 {
protected static $ins = NULL;
public static function getIns() {
if (self::$ins === null) {
self::$ins = new self();
}
return self::$ins;
}
// 添加final,所以这个方法不能被覆盖!
final protected function __construct() {
}
// 禁止克隆
final protected function __clone() {
}
}
$s10 = Singleton6::getIns();
$s11 = clone $s10;
// Fatal error: Call to protected Singleton6::__clone()
现在克隆是不可能的。