如何创建相同属性值时复用同一object_id的Ruby类
Ruby同值对象复用类实现方案
问题核心
目标是实现与Symbol一致的实例复用逻辑(享元模式):相同入参调用构造方法返回同一实例(object_id一致)。Ruby默认的Class#new方法每次调用都会分配新内存生成独立对象,无法直接满足该要求;另外初始代码存在构造方法名错误,Ruby构造方法固定为initialize,写为init不会在调用new时自动执行属性赋值。
正确实现代码
class PixKey # 类层面缓存已创建的实例,key为传入的pix密钥值,value为对应实例 @instances = {} class << self # 重写默认构造入口,实现复用逻辑 def new(key) # 命中缓存直接返回已有实例,未命中则创建新实例存入缓存 @instances[key] ||= super(key).freeze end # 可选方法:用于清空实例缓存,适配测试或特殊业务场景 def clear_cache @instances.clear end end attr_reader :key # 屏蔽外部直接调用原始new方法,避免绕过缓存生成重复实例 private_class_method :new def initialize(key) # 存储key的冻结副本,避免外部传入的字符串被修改影响实例状态 @key = key.dup.freeze end # 补全相等判断逻辑,即使未来逻辑调整也能保证同key实例相等 def ==(other) other.is_a?(PixKey) && other.key == @key end alias eql? == # 重写hash方法,保证实例作为Hash键时行为符合预期 def hash [self.class, @key].hash end end
实现说明
- 第一次传入某key调用
PixKey.new时,会检查缓存,无对应实例则调用原始new方法创建实例,冻结后存入缓存再返回 - 后续传入相同key调用构造方法时,直接返回缓存中已存在的实例,object_id完全一致,和Symbol的复用行为完全匹配
- 冻结实例和内部key值,是为了防止运行时修改实例属性破坏缓存映射关系
- 重写
==、eql?、hash方法后,实例可以安全作为Hash键、Set元素使用,行为和值对象一致
测试验证
原RSpec测试用例可直接通过,相同key两次调用new返回的是同一对象,不会再出现object_id不一致的报错。
内容的提问来源于stack exchange,提问作者Davi Luis
相关产品推荐
相关产品推荐

