настройка оператора ruby ​​.new - PullRequest
2 голосов
/ 08 мая 2011

Допустим, у меня есть класс Foo, и конструктор принимает 2 параметра. Основываясь на этих параметрах, метод initialize выполняет некоторые тяжелые вычисления и сохраняет их как переменные в экземпляре класса. Объект создан.

Теперь я хочу оптимизировать это и создать кеш этих объектов. При создании нового объекта Foo я хочу вернуть существующий объект из кэша, если параметры совпадают. Как я могу это сделать?

В настоящее время у меня есть self.new_using_cache(param1, param2), но я бы хотел, чтобы это интегрировалось в обычный Foo.new(). Возможно ли это каким-либо образом?

Я также могу сделать вывод, что использование .new() в сочетании с кешем не совсем синтаксически правильно. Это будет означать, что метод должен называться new_or_from_cache().

осветление Это не только тяжелый расчет, он также предпочтителен из-за ограничения количества дублирующихся объектов. Я не хочу 5000 объектов в памяти, когда я могу иметь 50 уникальных объектов из кэша. Поэтому мне действительно нужно настроить метод .new, а не только кэшированные значения.

Ответы [ 5 ]

5 голосов
/ 08 мая 2011
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 голос
/ 08 мая 2011

Как то так?

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
1 голос
/ 08 мая 2011

Вот решение, которое я нашел, определив универсальный модуль кэширования.Модуль ожидает, что ваш класс реализует методы «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
0 голосов
/ 08 мая 2011

Как вы, вероятно, знаете, вы заново изобрели шаблон проектирования фабричного метода , и это совершенно правильное решение, использующее ваше имя для фабричного метода.На самом деле, вероятно, лучше сделать это без переопределения 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)
0 голосов
/ 08 мая 2011

Вы можете использовать переменную экземпляра уровня класса для хранения результатов предыдущих экземпляров объекта:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...