在很多系统中,会存在这样的情况:
- 对象数量非常多
- 对象之间大量属性是相同的
- 只有少量状态不同
如果每个对象都完整创建,就会导致:
- 内存占用巨大
- 对象创建成本高
- 系统性能下降
这时候就可以使用 享元模式(Flyweight)。
一、享元模式解决什么问题?
一句话:
通过共享对象,减少内存使用。
核心思想:
- 把对象中可共享的部分提取出来
- 多个对象共享同一份数据
- 只把不同的部分作为外部状态
关键词:
- 对象共享
- 内存优化
- 内部状态
- 外部状态
二、一个典型场景:文本编辑器
假设一个文档有 100万字符。
每个字符对象可能包含:
- 字符内容
- 字体
- 字号
- 颜色
- 位置
如果每个字符都是完整对象:
1000000 × 字体信息 × 样式信息
大量数据是重复的。
例如:
字符: "A"
字体: Arial
字号: 12
颜色: 黑
可能出现几十万次。
这就是典型的享元模式使用场景。
三、享元模式的核心思想
享元模式会把对象状态拆成两部分:
1️⃣ 内部状态(Internal State)
可共享、不会变化。
例如:
- 字符内容
- 字体
- 字号
- 样式
这些可以被多个对象共享。
2️⃣ 外部状态(External State)
不可共享,每次不同。
例如:
- 文档位置
- 行号
- 列号
这些由外部传入。
四、享元模式结构
典型结构:
Flyweight
|
ConcreteFlyweight
|
FlyweightFactory
关键组件:
- Flyweight:共享对象接口
- ConcreteFlyweight:具体享元对象
- FlyweightFactory:享元工厂(负责缓存)
五、Python 实现示例
我们实现一个"字符享元"。
1️⃣ 享元对象
python
class CharacterFlyweight:
def __init__(self, char, font, size):
self.char = char
self.font = font
self.size = size
def render(self, position):
print(f"{self.char} ({self.font}, {self.size}) at {position}")
注意:
char/font/size→ 内部状态position→ 外部状态
2️⃣ 享元工厂
python
class FlyweightFactory:
def __init__(self):
self._cache = {}
def get_character(self, char, font, size):
key = (char, font, size)
if key not in self._cache:
self._cache[key] = CharacterFlyweight(char, font, size)
return self._cache[key]
这里就是核心:
相同对象只创建一次。
3️⃣ 使用方式
python
factory = FlyweightFactory()
c1 = factory.get_character("A", "Arial", 12)
c2 = factory.get_character("A", "Arial", 12)
c1.render(10)
c2.render(20)
注意:
c1 is c2
这两个对象是 同一个实例。
六、享元模式的关键:对象缓存
享元模式本质上是:
对象缓存 + 对象共享
最常见实现方式:
- 字典缓存
- 对象池
- 单例对象集合
七、Python 中的"天然享元"
Python 其实已经在很多地方使用享元思想。
例如:
小整数缓存
python
a = 10
b = 10
print(a is b)
结果:
True
因为 Python 会缓存 -5 到 256 的整数。
字符串驻留(String Interning)
python
a = "hello"
b = "hello"
print(a is b)
很多情况下也是同一个对象。
这其实就是享元思想。
八、真实项目中的应用
享元模式非常适合:
1️⃣ 游戏开发
大量:
- 子弹
- 粒子
- 树木
- 草地
模型数据可以共享。
2️⃣ UI 系统
大量:
- 图标
- 字体
- 样式
3️⃣ 缓存系统
例如:
- 配置对象
- 模板对象
- 常用数据
4️⃣ ORM 框架
数据库字段、表结构元数据通常会共享。
九、享元模式优缺点
✅ 优点
- 大幅减少内存使用
- 减少对象创建成本
- 提升系统性能
❌ 缺点
- 系统结构变复杂
- 外部状态管理困难
- 不适合状态复杂对象
十、什么时候使用享元模式?
适合:
- 大量对象
- 对象大部分状态相同
- 系统内存压力大
不适合:
- 对象差异很大
- 状态复杂
- 对象数量不多
经验法则:
当对象数量达到几十万甚至百万级时,才值得考虑享元模式。
十一、一句话总结
享元模式的本质是:
共享不变数据,分离变化数据。
用一句更直白的话:
不要重复创建相同对象。