メール出し忘れてた。
消える前に「バカが征く」ミラー (もう消えたけど)。
無闇に遅延初期化っつーのはどうかと思います。 def getFoo if @foo.nil? @foo = createFoo else @foo end end こういうの。初期化しておくんならコンストラクタとかでやっとくのが本道じゃ ないですかね。計算に時間がかかるとかがあれば別でしょうけど。
想像してみると、確かに initialize で初期化するほうがよいような気がします。 ではそれはなぜ「本道」なのかをできるだけ論理的に考えてみました。
まず、そういうもんだって言えば確かにそうです。 コンストラクタって言うくらいなんだからデータ構造を作るのに使うべきでしょう。 また読むほうもコンストラクタにはそういう役割があるだろうと期待しているので、 その期待に沿って書いたほうが読みやすいと感じるはずです (消極的な理由)。
またもう一つ、動的な構造をソースコードから想像するのが簡単になるという点が 挙げられます。データ構造を理解するのはコード読みの中でも根幹にあたりますから、 データ構造を想像しやすいコードは読みやすいでしょう。 従ってインスタンス変数はコンストラクタで初期化すべきです (積極的な理由)。
Ruby のように変数型のない言語では第二点は特に重要です。 変数が型ありの場合は、コンストラクタで代入していなくとも 変数定義さえ上のほうにあれば何が入るのかはとりあえずわかるからです。
逆に言うと、変数に型のない言語で読みやすいコードを書こうと思うと コンストラクタだけでデータ構造がわかるようにするほうがよい、 そうするように矯正される。かもしれません。単にわかりにくいコードに なるだけかもしれません。
ちなみに Ruby で遅延初期化をするときは次のように書くのが簡潔です。
@ivar ||= make_object()
また ||= を使う場合に限り @ivar を初期化していなくても警告が出ません。
~/c/tmail % ruby -vwe '@ivar = 0 unless @ivar' ruby 1.8.0 (2003-03-28) [i686-linux-gnu] -e:1: warning: instance variable @ivar not initialized ~/c/tmail % ruby -vwe '@ivar ||= 0' ruby 1.8.0 (2003-03-28) [i686-linux-gnu]
ただしこれはモジュール関数の中などでやむなく遅延初期化せざるを 得ない状況を救済するためではないかと思われます。
# 例 module FileUtils module_function def verbose_output( msg ) @output ||= $stderr # initializeが使えないので遅延初期化できない @output.puts(msg) end end
そうだ、initialize はクラス定義の最初に置くべきでしょう。 最初のころは「private だから」つーことで一番下に書いてたんですが、 前述のような理由を考えると initialize が先頭にないとまず意味が ありません。せっかく initialize が自動的に private になるように なってるんだから、その機能を活用しましょう。
あ、でもクラスの特異メソッドは initialize より前に置いてます。
include はいまだにブレがあるんですが、 だいたいクラス定義文の冒頭か initialize の直前ですかね。 冒頭に置く理由は「スーパークラスの記述と近いから」で、 initialize の前に置く理由は 「include はインスタンスメソッドの記述 (のショートカット) だから」です。 だから一部でしか使わないメソッドのために include するときは 例外的にその直前で include する場合もあります。
require はファイル先頭。 最初の最初にファイル間の関係を理解するとき、 require が先頭にないと理解しづらいです。
Copyright (c) 2002-2007 青木峰郎 / Minero Aoki. All rights reserved.
私は、遅延評価のことを忘れてしまったりすることが多多あるため、initializeで初期化します。require は、メソッドの中に書いちゃうことが結構あるんですが ^^;
じゃあ、「遅延初期化は最後から一個前の武器だ」ってことで。
とか言いつつも遅延初期化使いまくってますけどね、ぼくは。
今日の話題は自戒を込めて書いてみました ^^;;;
attr_ 系は initialize の前ですかね。
おお。タイムリー。
昨日このようなコードを書きました。
module DateOld
def roku
@roku = ほにゃほにゃ
instance_eval {
def self.roku
@roku
end
}
@roku
end
end
でも、こう書いたほうがいいですね。
module DateOld
def make_roku
ほにゃほにゃ
end
def roku
@roku ||= make_roku
end
end
あ、でも make_roku は nil になることがある(@roku=nilでrokuは以後nilを返す)ので @roku ||= make_roku は採用しにくいな。