Edited at

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

More than 5 years have passed since last update.

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


参考