PHP与设计模式:单例模式详解与实战
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;
}
}
?>