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 ,一个对象
单例模式是非常有用的,特别是我们需要确保在整个请求的声明周期内只有一个实例存在。 典型的应用场景是,当我们有一个全局的对象(比如配置类)或一个共享的资源(比如事件队列)时。
你应该非常小心地使用单例模式,因为它非常自然地引入了全局状态到你的应用中,降低了可测试性。 在大多数情况下,依赖注入可以(并且应该)代替单例类。 使用依赖注入意味着我们不会在设计应用时引入不必要的耦合,因为对象使用共享的或全局的资源,不再需要耦合具体的类。
实际项目中像数据库查询,日志输出,全局回调,统一校验等模块。这些模块功能单一,但需要多次访问,如果能够全局唯一,多次复用会大大提升性能。这也就是单例存在的必要性。
单例模式的好处: