PHP 单例模式详解与实战
单例模式(Singleton Pattern)是软件设计中的一种常用模式,它确保一个类在整个应用中只有一个实例,同时提供全局访问点。单例模式广泛应用于日志记录、数据库连接、配置管理等场景。
1. 单例模式简介
- 定义:单例模式是一种创建型设计模式,它确保某个类只有一个实例,并且提供一个访问该实例的全局方法。
- 特点: 唯一性:单例类只能有一个实例。 全局访问点:提供一个静态方法,用于获取实例。 延迟加载:在首次访问时才创建实例。
2. 单例模式的实现步骤
- 私有化构造函数:防止外部通过 new 创建实例。
- 私有化克隆方法:禁止对象被克隆。
- 创建静态属性存储实例:用于保存类的唯一实例。
- 提供静态方法获取实例:确保实例的唯一性。
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. 单例模式的应用场景
- 数据库连接 数据库连接通常只需要一个实例,使用单例模式可以避免重复创建连接。
<?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)
?>
- 配置管理 在项目中,配置通常是全局共享的,使用单例模式可以确保配置管理的一致性。
<?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
?>
- 日志记录 日志记录器需要全局访问,但避免多次实例化。
<?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;
}
}
?>