自定义 ruby​​ .new 运算符

发布于 2024-11-05 11:20:14 字数 480 浏览 0 评论 0原文

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

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

我目前有一个 self.new_using_cache(param1, param2),但我希望将其集成到普通的 Foo.new() 中。 这有可能吗?

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

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

Let's say I have a class Foo and the constructor takes 2 parameters.
Based on these parameters the initialize method does some heavy calculations and stores them as variables in the instance of the class. Object created.

Now I want to optimize this and create a cache of these objects. When creating a new Foo object, I want to return a existing one from the cache if the parameters match. How can I do this?

I currently have a self.new_using_cache(param1, param2), but I would love to have this integrated in the normal Foo.new().
Is this possible in any way?

I can also deduct that using .new() combined with a cache is not really syntactical correct.
That would mean that the method should be called new_or_from_cache().

clarification
It's not just about the heavy calculation, it's also preferred because of limiting the amount of duplicate objects. I don't want 5000 objects in memory, when I can have 50 unique ones from a cache. So I really need to customize the .new method, not just the cached values.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

空名 2024-11-12 11:20:15

这是我通过定义通用缓存模块提出的解决方案。该模块期望您的类实现“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

Here's a solution I came up with by defining a generic caching module. The module expects your class to implement the "retrieve_from_cache" and "store_in_cache" methods. If those methods don't exist, it doesn't attempt to do any fancy caching.

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
尘世孤行 2024-11-12 11:20:15

像这样的东西吗?

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

Something like this?

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

Edited

To not create an instance when the parameters are the same as before,

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
探春 2024-11-12 11:20:15

您可以使用类级实例变量存储先前对象实例化的结果:

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

You could use a class-level instance variable to store results from previous object instantiations:

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
雨落□心尘 2024-11-12 11:20:15

正如您可能知道的那样,您已经重新发明了工厂方法设计模式,并且使用您的名字这是一个完全有效的解决方案对于工厂方法。事实上,如果其他人必须理解它,最好不要重新定义 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)

As you probably know you have reinvented the factory method design pattern and it's a perfectly valid solution using your name for the factory method. In fact, it's probably better to do it without redefining new if anyone else is going to have to understand it.

But, it can be done. Here is my take:

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

And this results in:

(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)
乖乖 2024-11-12 11:20:14
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,然后如果您确实想使用Class#new,则调用super

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

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)

You can actually define self.new, then call super if you actually want to use Class#new.

Also, this totally approach prevents any instantiation from ever occurring if a new instance isn't actually needed. This is die to the fact the initialize method doesn't actually make the decision.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文