240 lines
6.0 KiB
Markdown
240 lines
6.0 KiB
Markdown
# 技术文档:单例模式装饰器与全局环境管理
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
本文档介绍了基于 Python 的单例模式实现,通过自定义装饰器 `SingletonDecorator` 实现类的单例化,并结合 `DictObject` 构建可扩展的全局环境对象 `GlobalEnv`。该设计适用于需要全局唯一实例的场景,如配置管理、日志记录器、数据库连接池等。
|
||
|
||
---
|
||
|
||
## 依赖模块
|
||
|
||
- `appPublic.dictObject.DictObject`
|
||
一个字典式对象封装类,允许通过属性方式访问字典键值(类似 JavaScript 的对象行为)。
|
||
|
||
> ⚠️ 注意:确保已安装并正确配置 `appPublic` 包。
|
||
|
||
---
|
||
|
||
## 核心组件
|
||
|
||
### 1. `SingletonDecorator` 类
|
||
|
||
#### 功能说明
|
||
`SingletonDecorator` 是一个类装饰器,用于将任意类转换为“单例类”——即在整个程序生命周期中,该类只能存在一个实例。
|
||
|
||
#### 源码解析
|
||
|
||
```python
|
||
class SingletonDecorator:
|
||
def __init__(self, klass):
|
||
self.klass = klass # 被装饰的类
|
||
self.instance = None # 单例实例缓存
|
||
|
||
def __call__(self, *args, **kwds):
|
||
if self.instance is None:
|
||
self.instance = self.klass(*args, **kwds) # 第一次创建实例
|
||
return self.instance # 后续调用均返回同一实例
|
||
```
|
||
|
||
#### 使用方式
|
||
使用 `@SingletonDecorator` 装饰目标类即可:
|
||
|
||
```python
|
||
@SingletonDecorator
|
||
class MyClass:
|
||
def __init__(self, value):
|
||
self.value = value
|
||
```
|
||
|
||
无论多少次实例化,都只会返回同一个对象。
|
||
|
||
#### 特性
|
||
- 延迟初始化(Lazy Instantiation):仅在首次调用时创建实例。
|
||
- 线程不安全(本实现未加锁),适用于单线程或无需并发控制的场景。
|
||
- 支持构造参数传递,但**仅第一次有效**。
|
||
|
||
> ❗ 注意:后续实例化传入的参数不会影响已有实例状态。
|
||
|
||
---
|
||
|
||
### 2. `GlobalEnv` 全局环境类
|
||
|
||
#### 定义
|
||
|
||
```python
|
||
@SingletonDecorator
|
||
class GlobalEnv(DictObject):
|
||
pass
|
||
```
|
||
|
||
#### 功能说明
|
||
`GlobalEnv` 继承自 `DictObject` 并被 `SingletonDecorator` 装饰,因此具备以下特性:
|
||
|
||
- 全局唯一实例(单例)
|
||
- 支持动态属性赋值和访问(类似字典)
|
||
- 可作为应用级共享数据容器(如配置、上下文变量等)
|
||
|
||
#### 示例用法
|
||
|
||
```python
|
||
env = GlobalEnv()
|
||
env.user = "admin"
|
||
env.settings = {"debug": True}
|
||
|
||
another = GlobalEnv() # 获取相同实例
|
||
print(another.user) # 输出: admin
|
||
print(another is env) # 输出: True
|
||
```
|
||
|
||
---
|
||
|
||
## 测试示例(`__main__` 模块)
|
||
|
||
以下代码演示了 `SingletonDecorator` 的实际效果。
|
||
|
||
### 示例类定义
|
||
|
||
#### `Child` 类
|
||
```python
|
||
@SingletonDecorator
|
||
class Child(object):
|
||
def __init__(self, name):
|
||
print("child.init")
|
||
self.name = name
|
||
|
||
def __str__(self):
|
||
return 'HAHA:' + self.name
|
||
|
||
def __expr__(self): # 注:应为 __repr__,此处命名错误
|
||
print(self.name)
|
||
```
|
||
|
||
#### `Handle` 类
|
||
```python
|
||
@SingletonDecorator
|
||
class Handle(object):
|
||
def __init__(self, name):
|
||
self.name = name
|
||
|
||
def __expr__(self): # 同样应为 __repr__
|
||
print(self.name)
|
||
```
|
||
|
||
### 执行逻辑
|
||
|
||
```python
|
||
c = Child('me')
|
||
d = Child('he')
|
||
|
||
print(str(c), str(d)) # 输出: HAHA:me HAHA:me
|
||
```
|
||
|
||
> 尽管两次构造传参不同,但由于单例机制,`d` 实际上是 `c` 的引用,`name` 仍为 `'me'`。
|
||
|
||
```python
|
||
e = Handle('hammer')
|
||
f = Handle('nail')
|
||
|
||
print(str(e), str(f)) # 假设实现了 __str__,否则会报错
|
||
```
|
||
|
||
同样地,`f` 与 `e` 是同一实例,最终输出取决于 `Handle` 是否重写了字符串方法。
|
||
|
||
---
|
||
|
||
## 输出结果分析
|
||
|
||
运行上述测试代码的实际输出为:
|
||
|
||
```
|
||
child.init
|
||
HAHA:me HAHA:me
|
||
HAHA:me HAHA:me
|
||
```
|
||
|
||
> 因为 `Handle` 类未定义 `__str__()` 方法,直接调用 `str(e)` 将引发异常。此为示例中的潜在 Bug。
|
||
|
||
---
|
||
|
||
## 已知问题与改进建议
|
||
|
||
| 问题 | 描述 | 建议 |
|
||
|------|------|------|
|
||
| `__expr__` 应为 `__repr__` | Python 中正确的特殊方法名为 `__repr__` | 更正方法名为 `__repr__` |
|
||
| `Handle` 缺少 `__str__` 方法 | 导致 `str()` 调用失败 | 添加 `__str__` 或继承合适基类 |
|
||
| 参数忽略风险 | 后续构造参数无效且无警告 | 可添加日志提示或抛出警告 |
|
||
| 线程安全性 | 多线程下可能创建多个实例 | 加入线程锁(`threading.Lock`) |
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
本模块提供了一种简洁高效的单例实现方案,配合 `DictObject` 可构建灵活的全局环境管理系统。适用于中小型项目中的全局状态管理需求。
|
||
|
||
---
|
||
|
||
## 附录:完整修正版建议代码
|
||
|
||
```python
|
||
from appPublic.dictObject import DictObject
|
||
import threading
|
||
|
||
class SingletonDecorator:
|
||
def __init__(self, klass):
|
||
self.klass = klass
|
||
self.instance = None
|
||
self.lock = threading.Lock()
|
||
|
||
def __call__(self, *args, **kwargs):
|
||
if self.instance is None:
|
||
with self.lock:
|
||
if self.instance is None: # Double-checked locking
|
||
self.instance = self.klass(*args, **kwargs)
|
||
return self.instance
|
||
|
||
|
||
@SingletonDecorator
|
||
class GlobalEnv(DictObject):
|
||
pass
|
||
|
||
|
||
if __name__ == '__main__':
|
||
@SingletonDecorator
|
||
class Child:
|
||
def __init__(self, name):
|
||
print("Child.init")
|
||
self.name = name
|
||
|
||
def __str__(self):
|
||
return 'HAHA:' + self.name
|
||
|
||
def __repr__(self):
|
||
return f"Child({self.name!r})"
|
||
|
||
c = Child('me')
|
||
d = Child('he')
|
||
print(c, d) # HAHA:me HAHA:me
|
||
print(c is d) # True
|
||
|
||
@SingletonDecorator
|
||
class Handle:
|
||
def __init__(self, name):
|
||
self.name = name
|
||
|
||
def __str__(self):
|
||
return f"Handle({self.name})"
|
||
|
||
def __repr__(self):
|
||
return self.__str__()
|
||
|
||
e = Handle('hammer')
|
||
f = Handle('nail')
|
||
print(e, f) # Handle(hammer) Handle(hammer)
|
||
print(e is f) # True
|
||
```
|
||
|
||
---
|
||
|
||
✅ 推荐在生产环境中使用修正版本以避免常见陷阱。 |