设计模式Python版 单例模式
文章目录
- 前言
- 一、单例模式
- 二、单例模式实现方式
- 三、单例模式示例
- 四、单例模式在Django框架的应用
前言
GOF设计模式分三大类:
- 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。
- 结构型模式:关注类和对象之间的组合,包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式和代理模式。
- 行为型模式:关注对象之间的交互,包括职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
一、单例模式
单例模式(Singleton Pattern)
-
定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
-
解决问题:如何确保系统中一个类只能有一个实例?
-
使用场景:
- 当系统中需要一个类来控制资源的访问,确保资源不会因为多个实例的创建而产生冲突时。
- 当整个系统的配置信息存放在一个对象中,并由一个实例来进行管理时。
- 当需要限制一个类的实例只能有一个,比如数据库连接池、线程池、缓存等。
-
优点:
- 单例模式提供了对唯一实例的受控访问。也可以特定数量的实例。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源。
-
缺点:
- 单例类较难扩展,单例类的职责过重
- 如果运行环境提供了自动垃圾回收技术,可能被回收销毁
二、单例模式实现方式
方式一:懒汉式,线程不安全
- 使用类变量和类方法实现单例模式。
get_instance()
类方法负责创建和返回类的唯一实例。 - 在第一次调用
get_instance()
方法时实例化,在类加载时并不自行实例化,这种技术又称为延迟加载(Lazy Load)技术,即需要的时候再加载实例。 - 在多线程环境下可能会有问题
class TaskManager:
tm: "TaskManager" = None
def __init__(self):
pass
@classmethod
def get_instance(cls):
if cls.tm is None:
cls.tm = TaskManager() # 自行实例化
return cls.tm
# 使用单例
task_manager = TaskManager.get_instance()
方寸二:懒汉式,线程安全
- 上述方式一可能会遇到线程安全问题。即如果有两个线程同时检查到 cls.tm 为 None 并尝试创建一个新的 TaskManager 实例,这就会导致创建了多个实例。
- 还需要待进一步确认。因为在Python中,由于全局解释器锁Global Interpreter Lock,GIL的存在,即使是多线程程序,在任何给定时刻也只能有一个线程执行Python字节码。
- 增加线程锁定保证线程安装,但会影响性能
import threading
class TaskManager:
tm: "TaskManager" = None
lock = threading.Lock()
def __init__(self):
pass
@classmethod
def get_instance(cls):
with cls.lock: # 进行线程锁定
if cls.tm is None:
cls.tm = TaskManager()
return cls.tm
# 使用单例
task_manager = TaskManager.get_instance()
方式三:Python模块级别的变量
- 使用模块实现单例模式。Python的模块本身就是单例的,因为模块在第一次导入时会被加载并创建,之后的导入操作只是引用第一次创建的模块对象。
config
是一个模块级别的变量,它在模块第一次被导入时创建,之后的导入操作都会使用这个已经创建的实例。
# 模块 my_config.py
class Config:
def __init__(self):
pass
config = Config()
# 在其他文件中使用
from my_config import config
推荐:方式三 > 方式二 > 方式一
三、单例模式示例
使用模块实现单例模式
- 将负载均衡器LoadBalancer设计为单例类,其中包含一个存储服务器信息的集合,每次随机选择一台服务器来响应客户端的请求
# 模块 balancer.py
import random
class LoadBalancer:
def __init__(self):
self.server_list = []
def add_server(self, server_name: str):
self.server_list.append(server_name)
def remove_server(self, server_name: str):
if server_name in self.server_list:
self.server_list.remove(server_name)
def get_server(self):
return random.choice(self.server_list)
load_balancer = LoadBalancer()
- 在其它文件中使用单例,客户端测试代码:
from balancer import load_balancer
load_balancer.add_server("server 1")
load_balancer.add_server("server 2")
load_balancer.add_server("server 3")
load_balancer.add_server("server 4")
for i in range(10):
server = load_balancer.get_server()
print(f"分发请求至服务器:{server}")
### 输出结果
分发请求至服务器:server 4
分发请求至服务器:server 3
分发请求至服务器:server 2
分发请求至服务器:server 3
分发请求至服务器:server 1
四、单例模式在Django框架的应用
配置对象(Settings)
- Django的配置对象是全局的,整个项目只有一个settings实例,这个实例包含了项目的所有配置信息。
- Django启动时加载配置文件,并将其作为一个单例供整个系统使用。
# 模块 django/conf/__init__.py
...
settings = LazySettings()
# 在其他文件中使用
from django.conf import settings
if settings.DEBUG:
# Do something
...
您正在阅读的是《设计模式Python版》专栏!关注不迷路~