单例模式

| 选择喜欢的代码风格  

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 单例模式实例


<?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 ,一个对象

单例模式是非常有用的,特别是我们需要确保在整个请求的声明周期内只有一个实例存在。 典型的应用场景是,当我们有一个全局的对象(比如配置类)或一个共享的资源(比如事件队列)时。

你应该非常小心地使用单例模式,因为它非常自然地引入了全局状态到你的应用中,降低了可测试性。 在大多数情况下,依赖注入可以(并且应该)代替单例类。 使用依赖注入意味着我们不会在设计应用时引入不必要的耦合,因为对象使用共享的或全局的资源,不再需要耦合具体的类。

为什么用单例模式?


实际项目中像数据库查询,日志输出,全局回调,统一校验等模块。这些模块功能单一,但需要多次访问,如果能够全局唯一,多次复用会大大提升性能。这也就是单例存在的必要性。

单例模式的好处:

  1. 减少频繁创建,节省了 Cpu
  2. 静态对象公用,节省了内存。
  3. 功能解耦,代码已维护。

PHP 单例模式扩展阅读:




发表评论