はじめに:変数の寿命とは?
コードレビューでよく指摘しているシリーズです。
みなさん、コードを書くときに 変数の寿命 は意識していますか?
そもそも変数の寿命とは何でしょうか?
変数の寿命とは、 その変数が宣言されてから使われなくなるまでの長さ です。
そして、基本的に 変数の寿命は短ければ短いほど良い です。
備考:サンプルコードについて
この記事のコード例ではRuby(と、一部JavaScript)を使っていますが、考え方自体はプログラミング言語を問わず、広く適用できるはずです。
良い例と悪い例
変数の寿命を理解するために、悪い例と良い例を見てみましょう。
これは悪い例です。
# ❌ BAD
price = calc_price(items)
# ...
# ...
# ...
# ...
# ...
# ...
# ...
puts "#{price}円です"
こちらは良い例です。
# ✅ GOOD
# ...
# ...
# ...
# ...
# ...
# ...
# ...
price = calc_price(items)
puts "#{price}円です"
悪い例では変数が宣言されてから使われるまでにすごく間が空いています(つまり寿命が長い)。
一方、良い例では変数宣言の直後に変数が使われています(つまり寿命が短い)。
変数の寿命は短ければ短いほど読み手の負担が減る
変数の寿命が長いと、コードの読み手は、
😰この変数はいつ使われるんだろう?
😰この変数はどこで定義されてるんだろう?
😰この変数、途中で変更されてないかな?
・・・といった疑問や不安を抱えてしまいます。
反対に、変数の寿命が短ければ短いほど、上記のような不安や疑問は解消されます。
つまり、コードの可読性や保守性が良くなります。
というわけで、コードを書くときは変数の寿命に着目してみてください。
「変数の寿命はできるだけ短く」が原則です。
つまり、constで宣言すればいいってこと?→NO!
もしかすると、「JavaScriptならconstで宣言して再代入不可にできるから、const宣言すれば安心ってこと?」と思った人も中にはいるかもしれません。
ですが、配列のようなミュータブルなオブジェクトの場合、constで宣言しても変数の再代入ができなくなるだけで、中身の変更は可能です。
具体例を挙げるならこういうことです。
const arr = [1, 2, 3]
// ..
// ..
// ..
arr.push(4) // ←再代入しなくても変更できる
// ..
// ..
// ..
console.log(arr)
//=> [1, 2, 3, 4]
一方、JSの場合、文字列(string)や数値(number)のようなプリミティブ型のデータはイミュータブルなので中身の変更はできません。
ですが、変数の寿命が無駄に長いと、
😰この変数はいつ使われるんだろう?
😰この変数はどこで定義されてるんだろう?
といった問題は引き続き残ります。
ですのでやはり、変数の寿命は短いに越したことはない、ということになります。
インスタンス変数は寿命がめちゃくちゃ長い(ので多用しない)
上で書いたのは基本的にローカル変数の話です。
ローカル変数の寿命はどれだけ長くても、メソッドの実行が終わるまでです。
メソッドを飛び越えて読み書きすることはできません1。
ですが、インスタンス変数になると話は別です。
インスタンス変数の寿命は宣言されてから、そのインスタンスが破棄されるまでです。
なので、メソッドの実行が終わっても引き続き読み書きできます。
※Rubyでは@foo
のように、@
で始まる変数がインスタンス変数になります。
だから、こんなコードは書いちゃダメ🙅♂️
このように、インスタンス変数はローカル変数に比べるとずっと寿命が長いため、インスタンス変数を多用すると非常に見通しの悪いプログラムができあがります。
ゆえに、以下のようなインスタンス変数にがっつり依存したロジックを書くのはNGです。
# インスタンス変数にがっつり依存したロジックはNG!!
def run
collect_data
display_data
end
def collect_data
# ❌メソッド内でインスタンス変数にデータを詰める
@data = ['a', 'b', 'c']
end
def display_data
# ❌メソッド内でインスタンス変数を読み取って画面に出力する
@data.each do |str|
puts str.upcase
end
end
run
Railsだと、「Viewで使うデータだから」という理由で、コントローラ内であれもこれもインスタンス変数に格納しようとする人がいますが、こういったコードもやはりNGです。
(Railsのコントローラで使うインスタンス変数はViewでも読み書きできる、という意味で非常に寿命が長い)
class BlogsController < ApplicationController
def show
@blog = Blog.find(params[:id])
# ❌コメントも画面で表示するからインスタンス変数に格納、はNG!!
@comments = @blog.comments
end
end
「良くないのはわかったけど、じゃあ、いったいどうすればいいのさ?」と思った方は、以下の記事に改善例を載せているのでこちらをご覧ください。
(上のコード例もこちらの記事から引用したものです)
まとめ
というわけで、この記事ではコードの可読性や保守性を高めるために、変数の寿命はできるだけ短くしましょう、という話を書きました。
コードレビューをしていると、「変数の寿命をできるだけ短くする」というルールを徹底できていないコードをよく見かけます。
また、「楽にロジックが書けるから」という理由で、インスタンス変数を多用する人もよく見かけます。
「変数の寿命なんて今まで考えたことがなかった」という人は、ぜひこれから変数の寿命を意識するようにしてみてくださいね!
-
クロージャのような例外もありますが、ここではいったん除外します。 ↩