TODO: 同JAVA或其他语言一样,PHP也有单例模式,在说单例模式之前,让我们先看看 PHP 官方的几个静态变量的例子:
变量范围的另一个重要特性是静态变量(static variable)。静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。看看下面的例子:
<?php
function Test()
{
$a = 0;
echo $a;
$a++;
}
?>
本函数没什么用处,因为每次调用时都会将 $a 的值设为 0 并输出 0。将变量加一的 $a++ 没有作用,因为一旦退出本函数则变量 $a 就不存在了。要写一个不会丢失本次计数值的计数函数,要将变量 $a 定义为静态的:
<?php
function test()
{
static $a = 0;
echo $a;
$a++;
}
?>
现在,变量 $a 仅在第一次调用 test() 函数时被初始化,之后每次调用 test() 函数都会输出 $a 的值并加一。
静态声明是在编译时解析的
静态变量也提供了一种处理递归函数的方法。递归函数是一种调用自己的函数。写递归函数时要小心,因为可能会无穷递归下去。必须确保有充分的方法来中止递归。以下这个简单的函数递归计数到 10,使用静态变量 $count 来判断何时停止:
<?php
function test()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
?>
静态变量可以按照上面的例子声明。如果在声明中用表达式的结果对其赋值会导致解析错误。
<?php
function foo(){
static $int = 0; // 正确
static $int = 1+2; // 错误 (不可以为表达式)
static $int = sqrt(121); // 错误 (不可以为表达式)
$int++;
echo $int;
}
?>
单例模式和创建单例的静态方法 getInstance(). 请注意以下几点:
__construct() 被声明为 protected 是为了防止用 new 操作符在这个类之外创建新的实例。__clone() 被声明为 private 是为了防止用 clone 操作符克隆出新的实例.__wakeup() 被声明为 private 是为了防止通过全局函数 unserialize() 反序列化这个类的实例。getInstance() 使用后期静态绑定生成的。这允许我们对 Singleton 类进行继承,并且在取得 SingletonChild 的单例时不会出现问题。
<?php
class SingletonDemo
{
// 私有化构造方法
protected function __construct()
{
}
// 私有化clone方法
private function __clone()
{
}
//防止通过全局函数 unserialize() 反序列化这个类的实例
private function __wakeup()
{
}
// 保存实例的静态对象
public static $singleInstance;
/**
* 声明静态调用方法
* 目的:保证该方法的调用全局唯一
*
* @return SingletonDemo
*/
public static function getInstance()
{
if (!self::$singleInstance) {
self::$singleInstance = new self();
}
return self::$singleInstance;
}
// 调用单例的方法
public function singletonFunc()
{
echo "Call singleton Method";
}
}
$singleInstance = SingletonDemo::getInstance();
$singleInstance->singletonFunc();
$singleInstance2 = SingletonDemo::getInstance();
$singleInstance2->singletonFunc();
// 校验是否是一个实例
var_dump($singleInstance === $singleInstance2); // true ,一个对象
单例模式是非常有用的,特别是我们需要确保在整个请求的声明周期内只有一个实例存在。 典型的应用场景是,当我们有一个全局的对象(比如配置类)或一个共享的资源(比如事件队列)时。
你应该非常小心地使用单例模式,因为它非常自然地引入了全局状态到你的应用中,降低了可测试性。 在大多数情况下,依赖注入可以(并且应该)代替单例类。 使用依赖注入意味着我们不会在设计应用时引入不必要的耦合,因为对象使用共享的或全局的资源,不再需要耦合具体的类。
实际项目中像数据库查询,日志输出,全局回调,统一校验等模块。这些模块功能单一,但需要多次访问,如果能够全局唯一,多次复用会大大提升性能。这也就是单例存在的必要性。
单例模式的好处: