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
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
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
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
3条答案
按热度按时间tyu7yeag1#
有两种方法。
第一个是让caller处理惰性对象创建。这是最简单的解决方案,也是Ruby代码中非常常见的模式。
第二个选择是让对象惰性地初始化自己。我们在实际对象周围创建一个委托对象来实现这一点。这种方法有点棘手,不推荐使用,除非您有无法修改的现有调用代码。
您也可以使用stdlib
delegate
在之上构建它。7dl7o3gd2#
如果你想延迟地计算代码片段,请使用代理:
你可以这样使用它:
你可以用这段代码来做任何复杂的初始化昂贵的东西:
它是如何工作的?示例化一个LazyProxy对象,该对象包含有关如何在Proc中构建一些昂贵对象的指令。如果您随后调用代理对象上的某个方法,它首先示例化昂贵对象,然后将方法调用委托给它。
yyyllmsg3#
在Ruby 3.x中,我使用gem
concurrent-ruby
。一个可能的惰性初始化用例如下所示: