定制ruby .new运算符

时间:2011-05-08 18:22:49

标签: ruby constructor

假设我有一个类Foo,构造函数需要2个参数。 基于这些参数,initialize方法执行一些繁重的计算,并将它们作为变量存储在类的实例中。对象已创建。

现在我想优化它并创建这些对象的缓存。在创建新的Foo对象时,如果参数匹配,我想从缓存中返回现有的Foo对象。我怎么能这样做?

我目前有一个self.new_using_cache(param1, param2),但我希望将其整合到正常Foo.new()中。 这有可能吗?

我还可以推断,使用.new()结合缓存并不是真正的语法正确。 这意味着该方法应该被称为new_or_from_cache()

澄清 这不仅仅是繁重的计算,它也是首选,因为限制了重复对象的数量。当我可以从缓存中获得50个唯一的对象时,我不希望内存中有5000个对象。所以我真的需要自定义.new方法,而不仅仅是缓存的值。

5 个答案:

答案 0 :(得分:5)

class Foo
  @@cache = {}

  def self.new(value)
    if @@cache[value]
      @@cache[value]
    else
      @@cache[value] = super(value)
    end
  end

  def initialize(value)
    @value = value
  end

end

puts Foo.new(1).object_id #2148123860
puts Foo.new(2).object_id #2148123820 (different from first instance)
puts Foo.new(1).object_id #2148123860 (same as first instance)

您实际上可以定义self.new,如果您确实想要使用super,请致电Class#new

此外,如果实际上不需要新实例,这种完全方法可以防止任何实例化发生。这是因为初始化方法实际上没有做出决定。

答案 1 :(得分:1)

这样的东西?

class Foo
  @@cache = {}
  def initialize prm1, prm2
    if @@cache.key?([prm1, prm2]) then @prm1, @prm2 = @@cache[[prm1, prm2]] else
      @prm1 = ...
      @prm2 = ...
      @@cache[[prm1, prm2]] = [@prm1, @prm2]
    end
  end
end

<强>被修改

当参数与之前相同时,不要创建实例

class Foo
  @@cache = {}
  def self.new prm1, prm2
    return if @@cache.key?([prm1, prm2])
    @prm1 = ...
    @prm2 = ...
    @@cache[[prm1, prm2]] = [@prm1, @prm2]
    super
  end
end

p Foo.new(1, 2)
p Foo.new(3, 4)
p Foo.new(1, 2)

# => #<Foo:0x897c4f0>
# => #<Foo:0x897c478>
# => nil

答案 2 :(得分:1)

这是我通过定义通用缓存模块提出的解决方案。该模块希望您的类实现“retrieve_from_cache”和“store_in_cache”方法。如果这些方法不存在,则不会尝试进行任何花哨的缓存。

module CacheInitializer
  def new(*args)
    if respond_to?(:retrieve_from_cache) &&
        cache_hit = retrieve_from_cache(*args)
      cache_hit
    else
      object = super
      store_in_cache(object, *args) if respond_to?(:store_in_cache)
      object
    end
  end
end

class MyObject
  attr_accessor :foo, :bar
  extend CacheInitializer

  @cache = {}

  def initialize(foo, bar)
    @foo = foo
    @bar = bar
  end

  def self.retrieve_from_cache(foo, bar)
    # grab the object from the cache
    @cache[cache_key(foo, bar)]
  end

  def self.store_in_cache(object, foo, bar)
    # write back to cache
    @cache[cache_key(foo, bar)] = object
  end

private
  def self.cache_key(foo, bar)
    foo + bar
  end
end

答案 3 :(得分:0)

您可以使用class-level instance variable来存储先前对象实例化的结果:

class Foo
  @object_cache = {}

  def initialize(param1, param2)
    @foo1 = @object_cache[param1] || @object_cache[param1] = expensive_calculation
    @foo2 = @object_cache[param2] || @object_cache[param2] = expensive_calculation
  end

  private
  def expensive_calculation
    ...
  enf
end

答案 4 :(得分:0)

你可能知道你已经彻底改造了the factory method design pattern,这是一个完全有效的解决方案,使用你的名字作为工厂方法。事实上,如果没有重新定义new,如果其他人必须要理解它,那么最好这样做。

但是,它可以做到。这是我的看法:

class Test

  @@cache = {}

  class << self
    alias_method :real_new, :new
  end

  def self.new p1
    o = @@cache[p1]
    if o
      s = "returning cached object"
    else
      @@cache[p1] = o = real_new(p1)
      s = "created new object"
    end
    puts "%s (%d: %x)" % [s, p1, o.object_id]
    o
  end

  def initialize p
    puts "(initialize #{p})"
  end

end
Test.new 1
Test.new 2
Test.new 1
Test.new 2
Test.new 3

这导致:

(initialize 1)
created new object (1: 81176de0)
(initialize 2)
created new object (2: 81176d54)
returning cached object (1: 81176de0)
returning cached object (2: 81176d54)
(initialize 3)