设计模式每日硬核训练 Day 15:享元模式(Flyweight Pattern)完整讲解与实战应用
🔄 回顾 Day 14:组合模式小结
在 Day 14 中,我们学习了组合模式(Composite Pattern):
- 适用于构建树状层级结构,使得“单个对象”和“对象集合”统一操作。
- 广泛用于文件系统、UI 控件树、组织结构等。
而今天的主角“享元模式”,是极致优化资源的代表。
享元模式(Flyweight Pattern)用于共享对象,减少内存占用,提升系统性能。
一、享元模式的核心动机
在系统中,如果存在大量“内容相同或相似”的对象,重复创建将造成资源浪费。
✅ 比如:
- 文本编辑器中成千上万个字符对象
- 游戏中上千棵树、子弹、砖块、粒子
- 地图系统中多个城市的图标
如果这些对象的“大部分状态相同”,我们就可以将它们共享起来,避免重复创建。
二、结构图(UML)
+----------------+ +--------------------+
| Flyweight |<-----| ConcreteFlyweight |
+----------------+ +--------------------+
| +operation() | | - intrinsicState |
+----------------+ | +operation() |+--------------------++----------------+ +-----------------------+
| FlyweightFactory |------>| Flyweight Pool |
+----------------+ +-----------------------+
| +getFlyweight() |
三、术语说明:
概念 | 含义描述 |
---|---|
Intrinsic State | 内部状态(可共享、不变) |
Extrinsic State | 外部状态(不共享,由客户端提供) |
Flyweight | 享元接口,定义共享对象应有操作 |
ConcreteFlyweight | 实现享元的具体对象 |
FlyweightFactory | 管理共享对象池(享元工厂) |
四、C++ 实现:字符渲染引擎
我们模拟一个文本渲染系统,文字中的每个字符都是一个对象,但字符本身可共享,坐标位置不可共享。
✅ Flyweight 接口
class Glyph {
public:virtual void render(int x, int y) = 0; // Extrinsicvirtual ~Glyph() = default;
};
✅ 共享对象:ConcreteFlyweight
class CharacterGlyph : public Glyph {char symbol_; // Intrinsic State
public:CharacterGlyph(char ch) : symbol_(ch) {}void render(int x, int y) override {std::cout << "绘制字符 '" << symbol_ << "' at (" << x << "," << y << ")\n";}
};
✅ 享元工厂
class GlyphFactory {std::unordered_map<char, std::shared_ptr<Glyph>> pool_;public:std::shared_ptr<Glyph> getGlyph(char ch) {if (!pool_.count(ch)) {pool_[ch] = std::make_shared<CharacterGlyph>(ch);}return pool_[ch];}
};
✅ 使用示例
int main() {GlyphFactory factory;std::string text = "AABAC";int x = 0;for (char ch : text) {auto glyph = factory.getGlyph(ch); // 共享对象glyph->render(x, 0); // 外部状态:坐标x += 10;}return 0;
}
输出:
绘制字符 'A' at (0,0)
绘制字符 'A' at (10,0)
绘制字符 'B' at (20,0)
绘制字符 'A' at (30,0)
绘制字符 'C' at (40,0)
五、实战应用场景
场景 | 享元内容说明 |
---|---|
文本编辑器 | 字符对象:字体、颜色共享,位置不共享 |
游戏地图渲染 | 大量树木、草丛、砖块共享对象,坐标单独记录 |
数据可视化系统 | 图标、图例、样式重复,减少渲染对象数量 |
图标资源池 | 图标素材在多个地方复用,统一缓存 |
数据库连接池 | 多个线程共用固定连接对象,提升资源复用率 |
六、优缺点分析
✅ 优点:
- 极大节省内存资源(大量对象时提升明显)
- 避免重复对象创建,提升性能
- 中央控制共享对象更可控
❗ 缺点:
- 管理复杂,需划分共享状态/外部状态
- 外部状态维护变复杂,需由使用者负责传入
七、与单例/对象池/享元的区别
模式 | 共享粒度 | 对象数量控制 | 核心目的 |
---|---|---|---|
单例 | 全局单一 | 只能有 1 个 | 全局唯一对象 |
对象池 | 共享对象集合 | 固定数量(动态管理) | 控制创建/复用的生命周期 |
享元 | 每类一个共享对象 | 可复用对象很多 | 减少对象创建,提高复用率 |
八、面试表达模板
“我们在文本渲染模块中使用了享元模式来复用字符对象,字符本身作为内部状态共享,坐标作为外部状态传入。通过 GlyphFactory 管理共享对象池,节省了内存占用并提升渲染性能,特别适合大量字符的 UI 场景。”
✅ 建议强调共享 vs 非共享状态划分、工厂管理、资源优化作用。
九、口诀记忆
“不变共用做享元,状态分离省内存;池中拿来直接用,场景重复效率增。”
十、明日预告:Day 16
责任链模式(Chain of Responsibility Pattern):请求沿链传递,节点动态决定处理权,构建灵活的处理流程结构。