假设我有一个类Foo
,构造函数需要2个参数。
基于这些参数,initialize方法执行一些繁重的计算,并将它们作为变量存储在类的实例中。对象已创建。
现在我想优化它并创建这些对象的缓存。在创建新的Foo对象时,如果参数匹配,我想从缓存中返回现有的Foo对象。我怎么能这样做?
我目前有一个self.new_using_cache(param1, param2)
,但我希望将其整合到正常Foo.new()
中。
这有可能吗?
我还可以推断,使用.new()
结合缓存并不是真正的语法正确。
这意味着该方法应该被称为new_or_from_cache()
。
澄清 这不仅仅是繁重的计算,它也是首选,因为限制了重复对象的数量。当我可以从缓存中获得50个唯一的对象时,我不希望内存中有5000个对象。所以我真的需要自定义.new方法,而不仅仅是缓存的值。
答案 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)