Techioz Blog

Rubyの遅延評価

概要

Ruby ではオブジェクトを作成する必要がある可能性がありますが、それはわかりません。また、オブジェクトの作成にはコストがかかる可能性があるため、あまり熱心に作成するつもりはありません。これは明らかに遅延読み込みのケースだと思います。誰かがメッセージを送信したときにのみ作成されないオブジェクトを定義するにはどうすればよいですか?オブジェクトはブロック内に作成されます。 Rubyで単純な遅延ロード/初期化を行う方法はありますか?これらは、オブジェクトの遅延初期化のさまざまなケースに対してさまざまなソリューションを提供するいくつかの gem でサポートされていますか?ご提案いただきありがとうございます。

解決策

方法は 2 つあります。

1 つ目は、呼び出し元に遅延オブジェクトの作成を処理させることです。これは最も単純な解決策であり、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

2 番目のオプションは、オブジェクト自体を遅延初期化させることです。これを実現するために、実際のオブジェクトの周囲にデリゲート オブジェクトを作成します。このアプローチは少し注意が必要であり、たとえば、変更できない既存の呼び出しコードがある場合を除き、お勧めできません。

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 デリゲートを使用してこれを構築することもできます。