享元模式在Go中通过结构体定义不可变内部状态、接口统一契约、sync.Map或带锁map实现工厂缓存、客户端传入外部状态来高效共享对象。

享元模式的核心是“共享不可变的内部状态,分离可变的外部状态”,在 Go 中没有传统面向对象的继承体系,但通过结构体、接口和指针,完全可以高效实现享元模式,尤其适合处理大量细粒度对象(如字符、图形节点、游戏实体)的内存优化场景。
用结构体定义享元(Flyweight)
享元对象本身应轻量、无状态或仅含不变的内部状态。Go 中推荐用值类型(struct)定义享元,避免不必要的指针开销;内部字段全部小写(私有),对外只暴露只读方法。
- 例如:一个字体样式享元,只存 fontFamily、size、weight 等固定属性
- 不保存颜色、位置、内容等随上下文变化的字段——这些属于外部状态,由客户端传入
- 享元结构体应实现一个接口(如 Flyweight),统一操作契约
用 sync.Map 或 map + RWMutex 实现享元工厂
享元工厂负责缓存和复用已有享元,避免重复创建。Go 标准库的 sync.Map 适合高并发读多写少场景;若需更可控的键构造或复杂查找逻辑,可用带读写锁的普通 map。
- 键建议用结构体(如 FontKey{Family: "Arial", Size: 14, Weight: "bold"} ),支持直接比较和哈希
- 首次请求时新建享元并存入;后续相同键直接返回已有实例(注意:返回的是指针,确保共享同一份数据)
- 无需手动管理生命周期——Go 的 GC 自动回收无人引用的享元
外部状态由客户端持有并传入操作方法
享元自身不保存上下文相关数据。每次使用时,客户端将外部状态(如坐标、文本内容、渲染颜色)作为参数传给享元的方法。
立即学习“go语言免费学习笔记(深入)”;
- 例如:glyph.Render(x, y, content, color) —— x/y/color/content 都是外部状态
- 这样同一个享元实例可被成千上万个不同位置/内容的字符复用
- 既节省内存,又保持线程安全(享元无内部可变状态)
结合对象池(sync.Pool)进一步优化高频创建场景
如果享元本身虽轻量但创建频率极高(如每帧生成数千临时绘图指令),可在享元工厂之上叠加 sync.Pool 缓存已分配但暂时不用的享元指针,减少 GC 压力。
- 注意:sync.Pool 不保证对象复用,仅作性能补充,核心共享逻辑仍在工厂的 map 中
- Pool 中的对象不能持有长生命周期引用,避免内存泄漏
- 通常只对“临时+高频+结构简单”的享元才启用 Pool,多数场景只需工厂缓存即可
基本上就这些。Go 的享元模式不依赖继承,重在设计意识:把变与不变分开,用值语义建模共享部分,用组合和参数传递承载上下文。写起来干净,跑起来省内存。










