Appearance
享元模式(Flyweight Pattern)
1. 官方定义
"Use sharing to support large numbers of fine-grained objects efficiently."
—— 《Design Patterns: Elements of Reusable Object-Oriented Software》(GoF, 1994)
核心:通过共享技术高效支持大量细粒度对象,减少内存占用。
2. 模式解释
核心思想
- 状态分离:将对象状态分为内部状态(可共享,如字符编码)和外部状态(不可共享,如位置坐标)。
- 对象池化:复用已存在的同类对象(如游戏中重复的树木、子弹模型)。
- 轻量化:减少因大量重复对象导致的资源消耗(内存、GC 压力)。
典型结构
plaintext
Client → FlyweightFactory
△
| manages
|
|<<Flyweight>>|
△
| stores intrinsic state
┌───────┴───────┐
| |
ConcreteFlyweight UnsharedFlyweight
优点
- 内存优化:显著减少对象数量(如 100 万字符仅需 26 个字母对象)。
- 性能提升:降低垃圾回收频率和初始化开销。
- 集中管理:享元工厂统一控制对象生命周期。
缺点
- 复杂度增加:需严格区分内部/外部状态,增加代码维护成本。
- 线程安全风险:共享对象需处理多线程访问(如连接池中的连接对象)。
3. 解决的问题
经典场景
- 文本编辑器
- 字符对象共享(每个字母仅存一份,位置/字体作为外部状态)。
- 游戏开发
- 大量重复游戏实体(如《Minecraft》中的方块渲染)。
- 图形绘制
- 重复图形元素(如 SVG 中相同图标的多处复用)。
4. 实现注意事项
关键实现点
- 状态分离设计
- 内部状态必须完全独立于使用场景(如字符编码与颜色无关)。
- 享元工厂管理
- 使用工厂类缓存和复用对象(如
FlyweightFactory.getCharacter('A')
)。
- 使用工厂类缓存和复用对象(如
- 不可变性保证
- 内部状态必须不可变(否则共享会导致数据污染)。
代码示例(文本编辑器)
java
// 享元接口
interface Character {
void draw(int x, int y, String color); // 外部状态:坐标和颜色
}
// 具体享元(内部状态:字符)
class ConcreteCharacter implements Character {
private final char symbol; // 内部状态(不可变)
public void draw(int x, int y, String color) {
System.out.printf("Draw '%s' at (%d,%d) with %s\n", symbol, x, y, color);
}
}
// 享元工厂
class CharacterFactory {
private Map<Character, Character> pool = new HashMap<>();
public Character getCharacter(char c) {
if (!pool.containsKey(c)) {
pool.put(c, new ConcreteCharacter(c));
}
return pool.get(c);
}
}
// 客户端调用
CharacterFactory factory = new CharacterFactory();
Character a = factory.getCharacter('A');
a.draw(10, 20, "red"); // 外部状态由客户端传递
5. 实现变体
类型 | 特点 | 适用场景 |
---|---|---|
单纯享元 | 所有对象均可共享(仅内部状态) | 如字符、图标等不可变对象 |
复合享元 | 组合多个享元对象(如单词由字符组成) | 需要组合结构的场景 |
线程局部享元 | 为每个线程维护独立享元池(避免锁竞争) | 高并发环境 |
6. 相似模式对比
模式 | 核心区别 |
---|---|
单例模式 | 单例确保全局唯一对象,享元允许多个共享对象 |
对象池模式 | 对象池管理临时对象(如数据库连接),享元管理长期复用的不可变对象 |
原型模式 | 通过克隆生成新对象,享元通过共享复用现有对象 |
7. 组合使用场景
常见搭配模式
- 工厂模式
- 场景:通过享元工厂统一创建和管理共享对象。
- 组合模式
- 场景:共享树形结构的叶子节点(如 UI 组件库中的重复按钮)。
- 状态模式
- 场景:共享状态对象(如游戏角色的行走/奔跑状态)。
总结
享元模式是大规模对象复用的内存救星,其核心在于分离内部/外部状态并通过工厂集中管理共享池。在需要处理海量相似对象的系统中(如大型游戏、文档处理),它能显著降低资源消耗,但需谨慎设计状态分离策略以避免线程安全问题。Java 的 String
常量池是享元模式的经典实践。```