13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

nil や false でも遅延初期化したい!

Last updated at Posted at 2014-05-16

リファクタリング:Rubyエディション の輪読会をやっているんですが、
本日この本で知った小ネタをメモしておきます。

Ruby でインスタンス変数を遅延初期化するときには、 ||= というイディオムをよく使いますよね。
ここでの遅延初期化とは initialize (コンストラクタ) で事前にインスタンス変数を初期化しておくのではなく、
インスタンス変数に初めてアクセスする際に初期化することです。

class SoulGem
  def clearness
    @clearness ||= 100
  end

  def get_polluted
    @clearness = clearness - 1
  end
end

初回のアクセスでインスタンス変数に値が代入され、以降のアクセスでは既に代入された値が返されます。

ただし代入された値が nil や false (すなわち偽) の場合は、毎回右辺の値が代入されてしまいます。

class Magica < ActiveRecord::Base
  def best_friend
    # Magica.find_by 実行時には SQL が発行される。
    @best_friend ||= Magica.find_by(best_friend_id: self.id)
  end
end

Magica オブジェクトが (叛逆公開前の) マミさんの場合など
Magica.find_by(...) の結果が nil の場合、
せっかく遅延初期化をしようとしているのに best_friend が呼ばれる度に find_by が実行されます。
その結果、毎回 SQL が発行されてしまいます。あたしってほんとバカ……。

その場合は Object#instance_variable_defined? を使うと幸せになれそうです。
インスタンス変数が定義されていない場合のみ初期化するのです。

class Magica < ActiveRecord::Base
  def best_friend
    unless instance_variable_defined?(:@best_friend)
      @best_friend = Magica.find_by(best_friend_id: self.id)
    end
    @best_friend
  end
end

これで初期化時の値が nil や false になりうる場合でも、安心して遅延初期化ができますね!

参考

13
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?