防止Ruby中的重复对象

时间:2012-01-28 04:49:49

标签: ruby singleton

这不是一个单身人士,但它很接近,所以我想这很常见。我有一个类(Foo),其中的实例对应于具有唯一ID的外部数据结构。我想确保没有两个Foo实例可以具有相同的ID - 如果使用相同的id值调用构造函数,使用该ID调用原始 Foo实例,并且只更新所有其他值。换句话说,比如:

class Foo
  def initialize(id, var1, var2)
    if Foo.already_has? id
      t = Foo.get_object(id)
      t.var1 = var1
      t.var2 = var2
      return t
    else
      @var1 = var1
      @var2 = var2
    end
end

我可以想到两种方法:

  1. 我可以将Foo的所有实例数组保存为类级变量,然后在初始化方法结束时调用foo_instances.push(self)。这让我觉得很难看。

  2. 我相信Ruby已经跟踪某个数组中每个类的实例 - 如果是这样,这是否可以访问,它会比#1更好吗?

  3. ??? (Ruby似乎支持一些灵活的[meta-]编程技巧,所以如果已经有一种整洁的方法我不会感到惊讶。

1 个答案:

答案 0 :(得分:2)

您可以覆盖对象中的Foo.new,并在那里执行任何操作:

class Foo
  def self.new(id, var1, var2)
    return instance if instance = self.lookup
    instance = self.allocate
    instance.send :initialize, var1, var2
    return self.store(instance)
  end
end

显然,您也可以使用不同的类方法来获取对象;将initialize方法设为私有,以帮助阻止意外分配。

这样你每个ID只有一个实例,这通常比你提出的模式痛苦得多。

你仍然需要实现storelookup位,并且没有什么比你班级中的HashArray更好的了。

您想要考虑将您存储在类中的内容包装在WeakRef实例中,但仍然返回真实对象。这样,该类可以强制执行唯一性,而不会限制所使用的每个ID始终保留在内存中。

这不适合你的每个版本的情况,但对某些人来说肯定是这样。一个例子:

  # previous code omitted ...
  return store(instance)
end

def self.store(instance)
  @instances ||= {}
  @instances[instance.id] = WeakRef.new(instance)
  instance
end

def self.lookup(id)
  @instances ||= {}
  if weakref = @instances[id] and weakref.weakref_alive?
    return weakref.__getobj__  # the wrapped instance
  else
    return nil
  end
end