Ruby で遅延初期化処理付き getter を一発定義
こんな感じでしょうか?
class Module def attr_reader_with_initializer(name, initializer_name = nil) name = name.to_s variable_name = "@#{name}" first_accessor = "#{name}_reader_with_initializer".to_sym second_accessor = "#{name}_reader".to_sym attr_reader name.to_sym alias_method(second_accessor, name) define_method(first_accessor){ unless instance_variable_get(variable_name) then if block_given? then value = yield else unless initializer_name then initializer_name = "create_#{name}" end value = method(initializer_name.to_sym).call() end instance_variable_set(variable_name, value) end instance_eval <<-EOD class << self alias :#{name} :#{second_accessor} end EOD instance_variable_get(variable_name) } alias_method(name, first_accessor) end end
もっとスマートに書けそうな気もするんだけどとりあえず.
使い方は以下の通り.
class Hello attr_reader_with_initializer(:message) { Time::now.strftime("Hello @ %Y/%m/%d") } end class Hello2 def create_message Time::now.strftime("Hello @ %Y/%m/%d") end attr_reader_with_initializer(:message, :create_message) end
遅延初期化してるのに getter の速度が遅くならないなんて世界中の遅延初期化大好きっ子が大喜びしそうです.そんな人が何人いるかは知りませんが…
でも気に入らない点がいくつか.
- メソッド名を勝手に使ってる
- 文字列を引数にした instance_eval を使ってる
- 高速な getter を定義するために Module#attr_reader() に頼ってる
ってあたりでしょうか?
標準の Module#attr_reader() を仕様変更する方向で実装すれば全部解決しそうな予感もします.
…と思ったけど Module#attr_reader() は複数の引数を渡された場合それら全部の getter を定義する仕様なので合わないですね.