Python 设计模式 —— 单例模式

单例模式的概念

单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例.

核心概念

想象一下公司的 CEO 职位 – 整个公司只能有一个 CEO,无论哪个部门需要向 CEO 汇报,他们访问的都是同一个 CEO 对象。单例模式就是编程世界中的”CEO 职位管理机制”.

为什么需要单例模式

在实际开发中,有些对象我们只需要一个实例就足够了,比如:

  • 配置管理器:整个应用程序共享同一套配置
  • 数据库连接池:避免重复创建连接,节省资源
  • 日志记录器:确保所有日志都写入同一个文件
  • 缓存系统:统一管理缓存数据

Python 具体实现方法

模块

Python 的模块默认采用单例模式,也就是只初始化一次,多次 import 并不会多次导入. 例如:

Python
# 单例模式的实现方式:导入模块
# 数据库例子
class DatabaseConnection:
    def __init__(self):
        self.connection_string = "database://localhost:5432/mydb"
        print("数据库连接已创建")

    def query(self, sql):
        return f"执行查询: {sql}"

# 创建单例实例
db_instance = DatabaseConnection()

此时在另一个文件导入:

Python
from singleton_module import db_instance

# 只暴露 db_instance 接口,保证单例

print(db_instance.query("SELECT * FROM data;"))

此时 db_instance 是唯一的单例,接口只暴露它的话就实现了单例模式.

魔法方法 __new__

这里为 class 新增一个 _instance (什么名字都行),用于缓存真正的单例,第一次初始化就负责创建并存放单例,后面的初始化都只返回缓存即可:

Python
class DatabaseConnection:
    _instance = None

    def __init__(self):
        self.connection_string = "database://localhost:5432/mydb"
        print("数据库连接已创建")

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls)
            print("创建新实例")
        else:
            print("返回已有实例")

        return cls._instance

    def query(self, sql):
        return f"执行查询: {sql}"


db1 = DatabaseConnection()
db2 = DatabaseConnection()

print(db1 is db2)

装饰器

使用 Python 的装饰器可以非常简洁地实现,并且可复用,更推荐这种做法,方便解耦.

Python
def singleton(cls):
    """单例装饰器"""
    instances = {}

    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)

        return instances[cls]

    return wrapper


@singleton
class DatabaseConnection:
    _instance = None

    def __init__(self):
        self.connection_string = "database://localhost:5432/mydb"
        print("数据库连接已创建")

    def query(self, sql):
        return f"执行查询: {sql}"


db1 = DatabaseConnection()
db2 = DatabaseConnection()

print(db1 is db2)

这里进行一下解析,装饰器一般通过返回一个内部函数,调用的时候就类似 DatabaseConnection = singleton(DatabaseConnection) ,原理还是和 __new__ 方法一样,创建缓存来存放单例.

单例模式的缺陷

  1. 需要单元测试的时候,无法使用单例模式,因为测试会更改单例的状态;
  2. 多线程情况下,由于两个线程之间同时调用 __new__ 的时候,可能同时创建两个不同的实例,会破坏单例模式,因此需要加线程锁.
  3. 需要多态的时候应该尽量避免单例模式.

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注