我遇到了 Ruby 的情况,可能需要创建一个对象,但不确定。由于创建对象的成本可能很高,所以我不太急于创建它。我认为这是延迟加载的一个明显例子。如何定义一个仅在有人向其发送消息时才创建的对象?该对象将在块中创建。有没有办法在 Ruby 中进行简单的延迟加载/初始化?这些东西是否受到某些 gems 的支持,它们为对象的延迟初始化的各种情况提供了不同的解决方案?谢谢您的建议!
有两种方法。
第一个是让 caller 处理惰性对象创建。这是最简单的解决方案,并且是 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
第二个选项是让 object 延迟初始化自身。我们围绕实际对象创建一个委托对象来实现此目的。这种方法有点棘手,不推荐,除非您有无法修改的现有调用代码。
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
delegate
来构建它。
如果您想延迟评估代码片段,请使用代理:
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 中构建某些昂贵对象的指令。如果您随后在代理对象上调用某些方法,它首先实例化昂贵的对象,然后将方法调用委托给它。
在 Ruby 3.x 中,我使用 gem
concurrent-ruby
。require 'concurrent'
# put expensive code inside a "future":
very_lazy = Concurrent::Promises.future { some_expensive_code_block }
# the "future" starts performing work in background
# use
puts very_lazy.value # blocks, until the "future" is ready
puts very_lazy.value # repeated calls just re-use existing value
如果我错了,请大家指正。