У меня возникла ситуация для Ruby, где возможно, что объект необходимо создать, но он не уверен. И поскольку создание объекта может быть дорогостоящим, я не слишком хочу его создать. Я думаю, что это явный случай для ленивой загрузки. Как я могу определить объект, который не создается только тогда, когда кто-то отправляет ему сообщение? Объект будет создан в блоке. Есть ли способ для простой ленивой загрузки/инициализации в Ruby? Поддерживаются ли эти вещи некоторыми драгоценными камнями, которые предоставляют различные решения для различных случаев ленивой инициализации объектов? Спасибо за ваши предложения!Оценка Ленивая в Ruby
ответ
Существует два способа.
Первый заключается в том, чтобы позвонить ручку создания ленивого объекта. Это самое простое решение, и это очень распространенный шаблон в коде Ruby.
class ExpensiveObject
def initialize
# Expensive stuff here.
end
end
class Caller
def some_method
my_object.do_something
end
def my_object
# Expensive object is created when my_object is called. Subsequent calls
# will return the same object.
@my_object ||= ExpensiveObject.new
end
end
Второй вариант, чтобы позволить объект сам Initialise лениво. Для этого мы создаем объект-делегат вокруг нашего фактического объекта. Этот подход немного сложнее и не рекомендуется, если у вас нет существующего кода вызова, который вы не можете изменить, например.
class ExpensiveObject # Delegate
class RealExpensiveObject # Actual object
def initialize
# Expensive stuff here.
end
# More methods...
end
def initialize(*args)
@init_args = args
end
def method_missing(method, *args)
# Delegate to expensive object. __object method will create the expensive
# object if necessary.
__object__.send(method, *args)
end
def __object__
@object ||= RealExpensiveObject.new(*@init_args)
end
end
# This will only create the wrapper object (cheap).
obj = ExpensiveObject.new
# Only when the first message is sent will the internal object be initialised.
obj.do_something
Вы также можете использовать STDLIB delegate
, чтобы построить это на вершине.
В первом примере мне нужно сохранить экземпляр класса Caller. Правильно? Но в чем разница для меня - сохранить экземпляр класса Caller или сохранить экземпляр Expensive класса? – demas
В первом примере класс 'Caller' является всего лишь примером того, как вы бы * использовали * класс ExpensiveObject. Разница: введите лень, где вы * используете * ExpensiveObject' (простое) или вводите лень в '' ExpensiveObject' * * (немного сложнее). – molf
@molf: всякий раз, когда вы переопределяете 'method_missing', вы * должны * также переопределять' reply_to? '(Или предпочтительно' response_to_missing? 'В 1.9.2). См. Http://blog.marc-andre.ca/2010/11/methodmissing-politely.html – Nemo157
Если вы хотите, чтобы лениво оценить куски кода, использовать прокси-сервер:
class LazyProxy
# blank slate... (use BasicObject in Ruby 1.9)
instance_methods.each do |method|
undef_method(method) unless method =~ /^__/
end
def initialize(&lazy_proxy_block)
@lazy_proxy_block = lazy_proxy_block
end
def method_missing(method, *args, &block)
@lazy_proxy_obj ||= @lazy_proxy_block.call # evaluate the real receiver
@lazy_proxy_obj.send(method, *args, &block) # delegate unknown methods to the real receiver
end
end
Вы затем использовать его как это:
expensive_object = LazyProxy.new { ExpensiveObject.new }
expensive_object.do_something
Вы можете использовать этот код, чтобы сделать сколь угодно сложную инициализацию дорогой материал:
expensive_object = LazyProxy.new do
expensive_helper = ExpensiveHelper.new
do_really_expensive_stuff_with(expensive_helper)
ExpensiveObject.new(:using => expensive_helper)
end
expensive_object.do_something
Как это работает? Вы создаете экземпляр объекта LazyProxy, в котором содержатся инструкции о том, как построить дорогой объект в Proc. Если вы затем вызываете какой-либо метод в прокси-объекте, он сначала запускает дорогостоящий объект, а затем делегирует вызов метода ему.
Вместо того, чтобы кататься самостоятельно, вы можете использовать [lazy.rb] (https://github.com/mental/lazy). Есть несколько примеров использования в книге [Ruby Best Practices] (http://oreilly.com/catalog/9780596523015/), см. Стр. 123 и вперед. –