← 返回题目列表

享元模式和单例、原型、缓存有什么区别?

高频 困难 第 15 / 25 题 更新于 2026/07/28
享元模式单例模式原型模式缓存

简化版

享元模式关注大量细粒度对象的共享复用;单例模式关注全局唯一实例;原型模式关注通过复制创建对象;缓存关注复用数据或结果。它们都可能减少创建成本,但设计目的和适用场景不同。

详细版

区别可以这样看:

  • 享元模式:共享相同内部状态的对象,外部状态由调用方传入。
  • 单例模式:确保某个类全局只有一个实例。
  • 原型模式:通过克隆已有对象来创建新对象。
  • 缓存:保存计算结果、查询结果或对象,避免重复获取。

享元模式可能使用缓存 Map 实现,但它更强调内部状态和外部状态拆分;缓存不一定要求这种状态拆分。

完整版教学

一、享元模式和单例模式

单例模式强调“一个类只有一个对象”。享元模式强调“同样内部状态的对象只创建一份”。

例如:

  • 配置中心客户端可能是单例。
  • 黑棋和白棋可以是两个享元。
  • 字体样式可能有很多个享元,每种样式一个。

享元对象不一定只有一个,它通常是一组对象池。对象池里每个 key 对应一个共享实例。

二、享元模式和原型模式

原型模式用于复制对象,适合创建成本高、结构复杂的对象。它的结果通常是新对象。

享元模式用于共享对象,适合复用相同内部状态。它的结果通常是已有对象。

对比:

对比点享元模式原型模式
核心动作共享克隆
目标减少重复对象简化复杂对象创建
对象关系多处引用同一对象得到新的独立对象
状态处理拆分内部/外部状态处理深浅拷贝

如果对象需要独立修改,用原型更合适;如果对象应该共享且不可变,用享元更合适。

三、享元模式和缓存

享元模式经常借助缓存实现,例如用 Map 保存享元对象。但缓存是更宽泛的概念。

缓存可以缓存:

  • 数据库查询结果。
  • HTTP 响应。
  • 计算结果。
  • 文件内容。
  • 业务对象。

享元模式缓存的是可共享的内部状态对象,并且会把外部状态剥离出去。

如果没有内部状态和外部状态拆分,只是为了避免重复查询,那通常是缓存,不是典型享元。

四、享元模式和对象池

对象池复用对象实例,常见于数据库连接池、线程池等。对象池里的对象通常有借出、归还、生命周期管理。

享元对象通常不需要借还,多个客户端可以同时共享同一个不可变对象。

连接对象一般带有可变状态和资源句柄,所以连接池不等同于享元模式。它们都复用对象,但复用语义不同。

五、为什么这些模式容易混淆

它们都可能带来“少创建对象”的效果,所以容易被混在一起。

但设计模式不能只看效果,要看核心意图:

  • 单例解决唯一性。
  • 原型解决复制创建。
  • 缓存解决重复获取。
  • 享元解决大量相似对象的共享。

享元最独特的标志是内部状态和外部状态的拆分。

六、面试回答建议

面试中可以这样答:

享元模式不是单纯缓存,也不是单例。它通常维护一组共享对象,每个对象对应一种内部状态;外部状态在调用时传入。单例强调全局唯一,原型强调复制创建,缓存强调复用结果,享元强调细粒度对象共享和状态拆分。

这个回答能把几个容易混淆的模式区分开。

七、常见误区与追问

四者都可能减少创建,却控制不同关系。单例控制某类全局只有1个实例;享元按状态键共享 K 个实例;原型复制已有对象得到新实例;缓存按键保存结果并允许失效。比如颜色享元可能有16个,显然不是单例,也不会像原型那样每次复制。

检查维度判定依据
单例 / 享元全局一个 / 按内部状态共享多个
原型 / 缓存复制新对象 / 保存并复用结果
Singleton=1;Flyweight=K;Prototype=request次副本

记忆钩子:单例管数量,享元管共享,原型管复制,缓存管命中。

  • 误区:享元工厂本身是单例才算享元。 工厂生命周期不决定模式,关键是按内部状态复用对象。
  • 追问:享元缓存与普通缓存有何交集? 工厂常用 Map 实现,但享元对象要求明确共享语义和状态拆分。
  • 误区:原型和享元都在复用同一个实例。 原型复用初始化方案并产生副本,享元直接共享实例。
  • 追问:缓存失效适用于享元吗? 键空间无界时可以淘汰,但仍在用的对象引用不应因此失效。
  • 追问:如何用身份数量区分? 问同一键是返回同一对象、一个新副本,还是可能过期的新结果。

八、加强记忆

区分享元、单例、原型和缓存时抓住关键词:享元共享类型状态,单例保证唯一对象,原型复制新对象,缓存保存复用结果。享元最不能丢的是“内部状态/外部状态”。