PHP项目单例与多例模式

wen PHP项目 2

PHP项目单例与多例模式详解

单例模式 (Singleton Pattern)

核心概念

确保一个类只有一个实例,并提供全局访问点。

PHP项目单例与多例模式

经典实现

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  # 每次获取新实例
  1. 单例模式适用于需要全局唯一实例的场景,但要注意线程安全和资源管理
  2. 多例模式适用于需要管理有限数量的同名但不同配置的实例
  3. 现代框架已经提供了强大的依赖注入容器,建议优先使用容器管理单例
  4. 注意避免在构造函数中进行耗时操作,使用延迟初始化优化性能
  5. 在测试环境中,考虑提供重置静态属性的方法以便于隔离测试

抱歉,评论功能暂时关闭!