文章

设计模式解密:单例模式的终极指南(PHP/Go双实现)

一、什么是单例模式?

单例模式(Singleton Pattern) 是一种创建型设计模式,确保一个类仅有一个实例,并提供全局访问点。其核心思想是通过控制实例化过程,避免资源重复消耗和状态不一致。

核心特征:

  1. 私有化构造函数(防止外部实例化)

  2. 静态属性保存唯一实例

  3. 全局访问入口

  4. 线程安全(多线程环境下)

单例模式结构图


二、适用场景

  • ✅ 数据库连接池管理

  • ✅ 应用配置中心

  • ✅ 日志记录器

  • ✅ 硬件设备访问控制

  • ✅ 缓存管理器


三、PHP实现方案

基础实现(线程安全版)

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

    // 私有化构造方法
    private function __construct() {
        $this->connection = new PDO(
            'mysql:host=localhost;dbname=test',
            'root',
            'password'
        );
    }

    // 防止克隆
    private function __clone() {}

    // 防止反序列化
    public function __wakeup() {
        throw new Exception("Cannot unserialize singleton");
    }

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

    public function query(string $sql): PDOStatement {
        return $this->connection->query($sql);
    }
}

// 使用示例
$db1 = DatabaseConnection::getInstance();
$db2 = DatabaseConnection::getInstance();
var_dump($db1 === $db2); // 输出 bool(true)

进阶技巧:Trait复用

trait SingletonTrait {
    private static $instance;

    private function __construct() {}
    private function __clone() {}
    public function __wakeup() {
        throw new Exception("Cannot unserialize singleton");
    }

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

class Logger {
    use SingletonTrait;

    public function log(string $message) {
        echo "[LOG] " . date('Y-m-d H:i:s') . " - $message\n";
    }
}

四、Go实现方案

标准实现(带并发安全)

package singleton

import (
    "sync"
)

type database struct {
    conn string
}

var (
    instance *database
    once     sync.Once
)

// 导出接口而非结构体
type Database interface {
    Query(sql string) string
}

func (db *database) Query(sql string) string {
    return "Executed: " + sql
}

func GetInstance() Database {
    once.Do(func() {
        instance = &database{
            conn: "mysql://user:pass@localhost:3306/db",
        }
    })
    return instance
}

// 使用示例
/*
db1 := GetInstance()
db2 := GetInstance()
fmt.Println(db1 == db2) // true
*/

带连接池的高级实现

type ConnectionPool struct {
    pool chan net.Conn
}

var (
    poolInstance *ConnectionPool
    mu           sync.Mutex
)

func GetConnectionPool(maxConn int) *ConnectionPool {
    if poolInstance == nil {
        mu.Lock()
        defer mu.Unlock()
        
        if poolInstance == nil { // 双重检查锁
            poolInstance = &ConnectionPool{
                pool: make(chan net.Conn, maxConn),
            }
            // 初始化连接池
            for i := 0; i < maxConn; i++ {
                conn, _ := net.Dial("tcp", "localhost:3306")
                poolInstance.pool <- conn
            }
        }
    }
    return poolInstance
}

五、关键实现差异对比

特性

PHP

Go

线程安全

默认单线程无需处理

必须使用sync.Once或互斥锁

实例存储

类静态属性

包级私有变量

接口隔离

非必须

推荐通过接口暴露方法

延迟初始化

自动实现

需显式控制

序列化防护

需要实现__wakeup()

自动通过包机制保护


六、模式优缺点分析

👍 优点:

  • 严格管控资源访问

  • 减少内存开销

  • 避免状态不一致

  • 提供统一访问入口

👎 缺点:

  • 违反单一职责原则

  • 单元测试困难(全局状态)

  • 可能隐藏过度耦合

  • 多线程环境复杂度增加


七、最佳实践建议

  1. 谨慎使用:仅在真正需要全局唯一性的场景使用

  2. 依赖注入:通过容器管理单例生命周期

防御性编程

// Go示例:防止反射破坏单例
var flag uint32
func GetInstance() Singleton {
    if atomic.LoadUint32(&flag) == 1 {
        return instance
    }
    // ...初始化逻辑
}
  1. 文档标注:明确标注单例类职责


八、常见面试题

  1. 如何防止通过反射创建新实例?

  2. 单例模式如何实现延迟加载?

  3. 分布式系统中单例是否仍然有效?

  4. 如何实现可配置的单例类?

  5. 单例模式与静态类的本质区别是什么?


九、扩展思考

  • 单例注册表:管理多个单例对象

  • 环境适配:根据运行环境返回不同实现

  • 生命周期管理:支持单例重置/销毁

// PHP单例注册表示例
class SingletonRegistry {
    private static $instances = [];

    public static function getInstance(string $class) {
        if (!isset(self::$instances[$class])) {
            self::$instances[$class] = new $class();
        }
        return self::$instances[$class];
    }
}

架构师提示:在现代框架(如Laravel、Gin)中,推荐使用依赖注入容器替代传统单例实现,以获得更好的可测试性和灵活性。

License:  CC BY 4.0