单例模式
单例模式(Singleton Pattern)是设计模式中一种创建型模式,广泛应用于软件开发中。一以下以故事化的方式,结合详细的技术讲解,介绍单例模式的背景、定义、适用场景,并提供python的示例代码。
故事1:皇帝的玉玺
在古代的龙国,皇帝是国家的唯一最高统治者,象征权力的玉玺也只有一块。无论多少大臣、多少事务需要盖章,所有人都必须使用同一块玉玺。皇帝下令:这块玉玺是独一无二的,不能有第二块!谁敢私自造玉玺,格杀勿论!
于是,为了确保玉玺的唯一性,皇帝派专人保管,任何人需要盖章时,都得向保管者申请使用这块玉玺。这样,全国上下都用同一块玉玺,保证了权力的统一性和一致性。
有一天,邻国的使者来访,带来了一个问题:如果多个大臣同时需要盖章,玉玺如何分配?皇帝想了想,决定让保管者记录玉玺的使用情况,确保每次只有一个人能拿到玉玺使用,其他人必须排队等待。这不仅保证了玉玺的唯一行,还避免了混乱。
这个故事中的玉玺,就是单例模式的完全体现:全局唯一、受控访问。
在古代的一个小村庄里,有一口古老的“智慧之井”,据说井水能赋予饮用者无穷的智慧。村民们都想喝到井水,但村长发现,如果每个人都随意打水,井水很快就会干涸。于是,村长宣布:全村只能有一个“水官”负责管理井水,每天只打一桶水,供大家享用。这个“水官”就是全村唯一的井水管理者,任何时候只有他能接触到井水,确保井水不会被滥用。
这个故事就像程序设计中的单例模式。在软件开发中,有些资源(如数据库连接、配置文件、线程池等),就像“智慧之井”,如果每次都创建新实例,会浪费资源或导致冲突。单例模式就像村里的“水官”,确保全局只有一个实例,统一管理资源。
单例模式是什么?
单例模式(Singleton Pattern)是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式的核心是控制对象的创建过程,确保系统中该类的对象始终只有一个。
解决什么样的问题
单例模式主要解决以下问题:
1、资源共享:避免多次创建对象导致的资源浪费(如数据库连接池、日志对象)
2、全局状态管理:需要一个全局唯一的对象来协调系统行为(如配置管理器、计数器)
3、控制并发访问:防止多个实例同时操作同一资源导致的数据不一致(如线程池)
4、减少系统开销:避免频繁创建和销毁对象,提升性能
适用场景
单例模式适用于以下场景
1、需要全局唯一实例的资源
2、需要控制资源访问
3、需要全局协调的场景
优缺点
优点:
缺点
实现方式
单例模式有几种常见实现方式
Python示例代码
以下展示几种Python实现单例模式的方式,并附上详细注释,为了贴合故事,代码以“皇帝的玉玺”作为类名
方式1:经典懒汉式(线程不安全)
class ImperialSeal:
# 私有类变量,存储唯一实例
_instance = None
def __new__(cls):
# 如果实例不存在,则创建
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
# 出事话只执行一次,防止重复初始化
if not hasattr(self, '_initialized'):
self._initialized = True
self.seal_name = "Dragon Seal"
def use_seal(self):
print(f"Using the {self.seal_name} to stamp a decree!")
# 测试代码
if __name__ == "__main__":
seal1 = ImperialSeal()
seal2 = ImperialSeal()
# True
print(f"Same instance: {seal1 is seal2}")
# Using the Dragon Seal to stamp a decree!
seal1.use_seal()
说明
方式2:线程安全的懒汉式(使用锁)
import threading
from threading import Lock
class ImperialSeal:
# 私有类变量,存储唯一实例
_instance = None
# 锁,用于线程安全
_lock = Lock()
def __new__(cls):
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if not hasattr(self, '_initialized'):
self._initialized = True
self.seal_name = "Dragon Seal"
def use_seal(self):
print(f"Using the {self.seal_name} to stamp a decree!")
def create_seal():
seal = ImperialSeal()
print(f"Created seal: {id(seal)}")
# 测试代码
if __name__ == "__main__":
threads = [threading.Thread(target=create_seal) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
seal1 = ImperialSeal()
seal2 = ImperialSeal()
# True
print(f"Same instance: {seal1 is seal2}")
# Using the Dragon Seal to stamp a decree!
seal1.use_seal()
说明
方式3:Python模块单例
Python的模块本身是天然的单例,因为模块只加载一次。以下展示如何利用模块实现单例
# 单例模式 - 模块级单例实现
# 通过在模块级别定义全局唯一实例来实现单例模式
class ImperialSeal:
def __init__(self):
self.seal_name = "Dragon Seal" # 玉玺名称
def use_seal(self):
print(f"使用 {self.seal_name} 盖章于圣旨!") # 使用玉玺盖章
# 定义全局唯一实例
seal = ImperialSeal()
# 测试代码
if __name__ == "__main__":
# 直接引用模块中的 seal 实例,模拟多次导入
seal1 = seal
seal2 = seal
print(f"是否为同一实例: {seal1 is seal2}") # 应输出 True
seal1.use_seal() # 输出: 使用 Dragon Seal 盖章于圣旨!
说明
故事续篇:玉玺的挑战
龙国的玉玺管理逐渐复杂,大臣们发现
皇帝召集智囊团,决定
这个故事告诉我们:单例模式虽然简单有效,但需谨慎使用,避免滥用导致维护困难
适用场景举例
1、日志管理器:全局唯一的日志对象,确保日志写入一致
import logging
# logging 模块天然单例
logger = logging.getLogger("app")
2、数据库连接池
import pymysql
class DBConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.conn = pymysql.connect(host="localhost",
user="root",
password="123456",
database="test")
return cls._instance
3、配置管理:全局读取配置,避免重复加载
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Config, cls).__new__(cls)
cls._instance.settings = {"api_key": "12345", "timeout": 30}
return cls._instance
注意事项