PHP 单例模式详解与实战

单例模式(Singleton Pattern)是软件设计中的一种常用模式,它确保一个类在整个应用中只有一个实例,同时提供全局访问点。单例模式广泛应用于日志记录、数据库连接、配置管理等场景。


1. 单例模式简介

  • 定义:单例模式是一种创建型设计模式,它确保某个类只有一个实例,并且提供一个访问该实例的全局方法。
  • 特点: 唯一性:单例类只能有一个实例。 全局访问点:提供一个静态方法,用于获取实例。 延迟加载:在首次访问时才创建实例。

2. 单例模式的实现步骤

  1. 私有化构造函数:防止外部通过 new 创建实例。
  2. 私有化克隆方法:禁止对象被克隆。
  3. 创建静态属性存储实例:用于保存类的唯一实例。
  4. 提供静态方法获取实例:确保实例的唯一性。

3. PHP 单例模式实现

以下是单例模式的完整代码示例:

<?php

class Singleton {
    // 静态属性,存放唯一实例
    private static $instance = null;

    // 私有化构造函数,禁止外部实例化
    private function __construct() {
        echo "Instance created." . PHP_EOL;
    }

    // 禁止克隆对象
    private function __clone() {}

    // 禁止反序列化
    private function __wakeup() {}

    // 静态方法,提供全局访问点
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

// 测试单例模式
$instance1 = Singleton::getInstance(); // 输出: Instance created.
$instance2 = Singleton::getInstance();

var_dump($instance1 === $instance2); // 输出: bool(true)
?>

4.代码解析

private static $instance

用于存储单例对象。初始值为 null。 private function __construct()

将构造函数私有化,阻止外部直接创建对象。 public static function getInstance()

静态方法,负责检查并创建实例。如果实例不存在(null),则创建实例;否则返回已有实例。 private function __clone() 和 private function __wakeup()

分别禁止克隆和反序列化,确保实例唯一性。


5. 单例模式的应用场景

  1. 数据库连接 数据库连接通常只需要一个实例,使用单例模式可以避免重复创建连接。
<?php

class Database {
    private static $instance = null;
    private $connection;

    private function __construct() {
        // 创建数据库连接
        $this->connection = new PDO('mysql:host=localhost;dbname=test', 'root', '');
    }

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function getConnection() {
        return $this->connection;
    }
}

// 测试
$db1 = Database::getInstance()->getConnection();
$db2 = Database::getInstance()->getConnection();

var_dump($db1 === $db2); // 输出: bool(true)
?>
  1. 配置管理 在项目中,配置通常是全局共享的,使用单例模式可以确保配置管理的一致性。
<?php

class Config {
    private static $instance = null;
    private $settings = [];

    private function __construct() {}

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function set($key, $value) {
        $this->settings[$key] = $value;
    }

    public function get($key) {
        return $this->settings[$key] ?? null;
    }
}

// 测试
$config = Config::getInstance();
$config->set('app_name', 'My Application');
echo $config->get('app_name'); // 输出: My Application
?>
  1. 日志记录 日志记录器需要全局访问,但避免多次实例化。
<?php

class Logger {
    private static $instance = null;

    private function __construct() {}

    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function log($message) {
        echo "[LOG]: $message" . PHP_EOL;
    }
}

// 测试
Logger::getInstance()->log('This is a log message.');
Logger::getInstance()->log('This is another log message.');
?>

6. 单例模式的优缺点

  • 优点: **节省资源:**避免重复创建对象,节省内存。 **全局访问点:**简化对象管理,提高代码可维护性。 **实现简单:**代码实现容易理解和扩展。
  • 缺点: **多线程问题:**在多线程环境中,需要注意线程安全(如PHP的WebSocket或CLI多线程)。 **隐藏依赖:**由于实例是静态获取,可能导致代码难以测试和维护。 **全局状态:**可能引入难以追踪的全局状态。

7. 多线程环境中的单例

在多线程环境下,单例模式需要通过加锁机制确保线程安全:

<?php

class ThreadSafeSingleton {
    private static $instance = null;
    private static $lock = false;

    private function __construct() {}

    public static function getInstance() {
        if (self::$instance === null) {
            // 加锁,确保线程安全
            if (!self::$lock) {
                self::$lock = true;
                self::$instance = new self();
                self::$lock = false;
            }
        }
        return self::$instance;
    }
}
?>