PHP项目单例与多例模式详解
单例模式 (Singleton Pattern)
核心概念
确保一个类只有一个实例,并提供全局访问点。

经典实现
class Database {
private static ?Database $instance = null;
private PDO $pdo;
// 私有化构造方法
private function __construct(array $config) {
$this->pdo = new PDO(
"mysql:host={$config['host']};dbname={$config['dbname']}",
$config['user'],
$config['pass']
);
}
// 禁止克隆
private function __clone() {}
// 禁止反序列化
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
// 获取实例
public static function getInstance(array $config = []): self {
if (self::$instance === null) {
self::$instance = new self($config);
}
return self::$instance;
}
public function query(string $sql): array {
return $this->pdo->query($sql)->fetchAll();
}
}
// 使用
$db = Database::getInstance(['host' => 'localhost', ...]);
$db2 = Database::getInstance(); // 返回同一实例
实际应用场景
// 1. 配置管理
class Config {
private static ?Config $instance = null;
private array $settings = [];
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function load(string $file): void {
$this->settings = parse_ini_file($file);
}
public function get(string $key, $default = null) {
return $this->settings[$key] ?? $default;
}
}
// 2. 日志记录
class Logger {
private static array $instances = [];
private string $channel;
private function __construct(string $channel) {
$this->channel = $channel;
}
public static function getInstance(string $channel): self {
if (!isset(self::$instances[$channel])) {
self::$instances[$channel] = new self($channel);
}
return self::$instances[$channel];
}
public function log(string $message): void {
echo "[{$this->channel}] $message\n";
}
}
多例模式 (Multiton Pattern)
核心概念
单例模式的扩展,维护多个命名实例的集合。
经典实现
class DatabaseConnection {
private static array $instances = [];
private string $name;
private PDO $pdo;
private function __construct(string $name, array $config) {
$this->name = $name;
$this->pdo = new PDO(
"mysql:host={$config['host']};dbname={$config['dbname']}",
$config['user'],
$config['pass']
);
}
private function __clone() {}
public static function getInstance(string $name, array $config = []): self {
if (!isset(self::$instances[$name])) {
self::$instances[$name] = new self($name, $config);
}
return self::$instances[$name];
}
public static function exists(string $name): bool {
return isset(self::$instances[$name]);
}
public static function remove(string $name): void {
unset(self::$instances[$name]);
}
public function getPdo(): PDO {
return $this->pdo;
}
}
// 使用
$primary = DatabaseConnection::getInstance('primary', [
'host' => '192.168.1.1',
'dbname' => 'main_db'
]);
$replica = DatabaseConnection::getInstance('replica', [
'host' => '192.168.1.2',
'dbname' => 'read_db'
]);
高级多例模式实现
abstract class Multiton {
private static array $instances = [];
protected function __construct() {}
final public static function getInstance(string $key = 'default'): static {
$class = get_called_class();
if (!isset(self::$instances[$class][$key])) {
self::$instances[$class][$key] = new static();
}
return self::$instances[$class][$key];
}
final public static function reset(): void {
$class = get_called_class();
unset(self::$instances[$class]);
}
final private function __clone() {}
}
// 具体实现
class DatabaseHandler extends Multiton {
public function connect(array $config): void {
// 连接逻辑
}
}
class CacheHandler extends Multiton {
public function set(string $key, $value): void {
// 缓存逻辑
}
}
// 使用
$db = DatabaseHandler::getInstance('primary');
$cache = CacheHandler::getInstance('redis');
实际项目中的最佳实践
线程安全考虑
class ThreadSafeSingleton {
private static array $instances = [];
private static array $locks = [];
public static function getInstance(string $key = 'default'): self {
// 双重检查锁定
if (!isset(self::$instances[$key])) {
self::$locks[$key] = self::$locks[$key] ?? new \stdClass();
synchronized(self::$locks[$key]) {
if (!isset(self::$instances[$key])) {
self::$instances[$key] = new self();
}
}
}
return self::$instances[$key];
}
}
延迟初始化优化
class LazySingleton {
private static ?self $instance = null;
public static function getInstance(): self {
return self::$instance ??= new self();
}
// 使用生成器实现懒加载
public static function getLazyInstance(): \Generator {
yield self::getInstance();
}
}
依赖注入容器中的使用
class ServiceContainer {
private static array $services = [];
private static array $factories = [];
public static function set(string $name, callable $factory): void {
self::$factories[$name] = $factory;
}
public static function get(string $name): object {
if (!isset(self::$services[$name])) {
self::$services[$name] = self::$factories[$name]();
}
return self::$services[$name];
}
public static function singleton(string $name, callable $factory): void {
self::set($name, function() use ($factory) {
static $instance = null;
return $instance ??= $factory();
});
}
}
// 注册服务
ServiceContainer::singleton('db', function() {
return new DatabaseConnection(['host' => 'localhost']);
});
ServiceContainer::singleton('cache', function() {
return new CacheManager(['driver' => 'redis']);
});
性能对比与选择建议
| 特性 | 单例模式 | 多例模式 |
|---|---|---|
| 实例数量 | 1个 | 有限个(lt;10) |
| 资源消耗 | 最小 | 较小 |
| 使用场景 | 全局唯一资源 | 多种配置的资源 |
| 测试难度 | 中等 | 更容易 |
| 扩展性 | 差 | 较好 |
选择建议
// 单例模式适用场景 - 配置管理 - 日志记录器 - 数据库连接池 - 缓存管理器 // 多例模式适用场景 - 多数据库连接 - 多文件系统连接 - 多API服务连接 - 多缓存层管理 // 避免使用的情况 - 需要频繁创建/销毁的对象 - 状态可变的对象 - 需要高度测试覆盖的代码
常见的陷阱与解决方案
循环依赖
// 错误示例
class ServiceA {
public function __construct() {
ServiceB::getInstance();
}
}
// 解决方案:使用注入容器
class ServiceProvider {
public function boot(): void {
app()->singleton('serviceA', function() {
return new ServiceA(app()->make('serviceB'));
});
app()->singleton('serviceB', function() {
return new ServiceB();
});
}
}
资源泄漏
class ManagedSingleton {
private static ?self $instance = null;
private bool $initialized = false;
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function cleanup(): void {
if ($this->initialized) {
// 清理资源
$this->initialized = false;
}
}
public function __destruct() {
$this->cleanup();
}
}
现代PHP框架中的实现
Laravel中的门面模式
use Illuminate\Support\Facades\Cache;
class UserService {
public function getUser(int $id): ?User {
return Cache::remember("user_{$id}", 3600, function() use ($id) {
return User::find($id);
});
}
}
Symfony中的服务容器
# services.yaml
services:
app.database_connection:
class: App\DatabaseConnection
arguments:
- '%database_host%'
- '%database_name%'
# 默认是shared: true(单例)
shared: true
app.mailer:
class: App\Mailer
shared: false # 每次获取新实例
- 单例模式适用于需要全局唯一实例的场景,但要注意线程安全和资源管理
- 多例模式适用于需要管理有限数量的同名但不同配置的实例
- 现代框架已经提供了强大的依赖注入容器,建议优先使用容器管理单例
- 注意避免在构造函数中进行耗时操作,使用延迟初始化优化性能
- 在测试环境中,考虑提供重置静态属性的方法以便于隔离测试
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播