享元模式(Flyweight Pattern) 是一种 结构型设计模式 ,旨在通过共享对象来有效支持大量细粒度对象的复用,从而减少内存占用和提高性能。其核心是 分离内部状态(可共享)与外部状态(不可共享),适用于存在大量重复对象且对象的大部分状态可以外部化的场景。
1. 核心角色
角色 | 说明 |
---|---|
享元接口(Flyweight) | 定义对象的方法,通常包含接收外部状态的操作。 |
具体享元类(Concrete Flyweight) | 实现享元接口,存储内部状态(可共享部分)。 |
享元工厂(Flyweight Factory) | 管理享元对象池,确保相同内部状态的对象唯一。 |
客户端(Client) | 维护外部状态,通过享元工厂获取对象并传递外部状态。 |
2. 代码示例:图形编辑器中的树对象
假设需要绘制大量树(Tree)对象,树的类型(名称、颜色)可共享,位置和年龄为外部状态。
步骤1:定义享元接口 
步骤2:实现具体享元类 
步骤3:实现享元工厂 
步骤4:客户端使用 
输出结果 
3. 享元模式类图 
4. 享元模式优缺点
优点 | 缺点 |
---|---|
减少内存占用,提升性能。 | 增加系统复杂度(需分离内外部状态)。 |
支持大量细粒度对象的高效管理。 | 线程安全问题需额外处理(如工厂类)。 |
5. 内部状态 vs. 外部状态
状态类型 | 说明 |
---|---|
内部状态(Intrinsic) | 对象可共享的部分,独立于使用场景(如树的类型、颜色)。 |
外部状态(Extrinsic) | 对象不可共享的部分,随上下文变化(如树的位置、年龄)。 |
6. 实际应用场景
-
文本编辑器
-
内部状态:字符的字体、颜色、大小。
-
外部状态:字符在文档中的位置。
-
-
游戏开发
-
内部状态:子弹类型、纹理。
-
外部状态:子弹的位置、速度。
-
-
Java 字符串池
String
类的常量池是享元模式的典型实现,相同字面量的字符串对象被复用。
7. 享元模式 vs. 其他模式
模式 | 区别 |
---|---|
单例模式 | 单例确保全局唯一实例;享元允许存在多个实例,但相同内部状态的实例被共享。 |
对象池模式 | 对象池复用对象生命周期;享元复用对象内部状态。 |
8. 总结
-
核心思想:通过共享内部状态减少对象数量,外部状态由客户端传递。
-
关键实现:享元工厂管理共享对象池,确保唯一性。
-
适用场景:存在大量相似对象且内存占用是瓶颈时优先考虑。