今 リファクタリング: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 になりうる場合でも、安心して遅延初期化ができますね!