Ruby で遅延初期化処理付き getter を一発定義(2)
http://d.hatena.ne.jp/sa-y/20051217 のコメントを参照.
なかださん最高♪ 私のへちょいプログラムとは比べものにならないですねぇ…
きっと世界中の遅延初期化マニアが抱き付いてキスしてきますよ?
インデントが崩れてるのとインスタンス変数が初期化済みの場合にnilを返してしまうのだけ修正したものを以下に置きます.
# なかださん作の遅延初期化処理付き Module#attr_reader() class Module alias original_attr_reader attr_reader def attr_reader(*names) unless block_given? return original_attr_reader(*names) end names.each do |name| ivar = "@#{name}" define_method(name) do value = instance_variable_get(ivar) unless value value = yield self instance_variable_set(ivar, value) end class << self; self; end.class_eval do original_attr_reader name end value end end end end if $0 == __FILE__ class Hello attr_reader(:message) { Time::now.strftime("Hello @ %Y/%m/%d") } end class Hello2 def create_message Time::now.strftime("Hello @ %Y/%m/%d") end attr_reader(:message) {|obj| obj.create_message} end p h = Hello.new, h2 = Hello2.new p h.message, h2.message p h.message, h2.message p h, h2 end
私のに比べた改善点は3つくらい.
- メソッド定義しておいて alias で差し替える処理から attr_reader で再定義するようになって無駄にメソッド名を浪費しないように変更
- いかにも無駄っぽかったinstance_eval()が特異クラスに対するclass_eval()に変更
- yield に self を渡すことでブロック内でインスタンスメソッドが呼べるように変更
2.は
class << self; self; end
で特異クラスのオブジェクトが取り出せるってのはどこかで読んで知っていたんだけど,これに class_eval() を組み合せるのは思い付かなかった.
3.はインスタンスメソッド呼べないと役に立たないと思いっていたのに思い付かなかった.Ruby的にはよく見かけるイディオムなのになぁ…
うーん,勉強になるなぁ.