こちらの記事の補足。
インスタンス変数とは
インスタンス変数は、その名の通りインスタンス(オブジェクト)が持つ変数。インスタンスメソッド内で変数名の頭に@
をつける。
インスタンスとオブジェクトはほとんど同じ意味。(詳しくはこちらの記事)
def initialize(name, age) @name = name @age = age end
同じクラスのインスタンスであっても、各インスタンスによって違う値を持つ。名前や年齢を考えてみればわかるように、偶然同じこともあるかもしれない。
変数のスコープ
変数には、スコープ(使える範囲)というものがある。
ローカル変数
メソッド内で定義した変数は、そのメソッド内でしか使うことができない。 反対に、メソッド外で定義した変数は、メソッドの中で使うことができない。
このような変数のことを、ローカル変数という。
class User def greet_ja my_name = "山田" puts "#{my_name}さん、こんにちは!" end def greet puts "Hello #{my_name}!" # エラー! # greet_jaメソッドの外で、ローカル変数my_nameを使うことはできない end end
インスタンス変数
一方、インスタンス変数のスコープは、インスタンス(オブジェクト)と同じになる。
同じインスタンスであれば、他のメソッドからでも呼び出して使うことができる。
class User def greet_ja @my_name = "山田" puts "#{@my_name}さん、こんにちは!" end def greet puts "Hello #{@my_name}!" # 他のメソッドからでも@my_nameを呼び出せる end end
(ただし、@my_nameへの代入実行後(上の例ではgreet_jaメソッドの実行後)でないと@my_nameは空っぽになってしまうことに注意)
何をインスタンス変数にするべきか
結論から言うと、そのオブジェクト(インスタンス)の属性といえるものをインスタンス変数にするべきだと思う。
たとえば下のようなUserクラスを考える。
class User def initialize(name, age) @name = name @age = age end def greet puts "Hello #{@name}!" end end
もしインスタンス変数を使わなかったら、greetメソッドは下のようになる。
# nameをメソッドの引数として渡してあげなければならない def greet(name) puts "Hello #{name}!" end
メソッドを実行するたびに毎回nameを引数で渡すとなると、面倒なだけでなく間違いの元にもなる。こういう値は、インスタンス変数としてインスタンス自身に持たせておくことで、必要なときにいつでも使えるようにしたほうがいい。
では、メソッドの引数になりそうなものを全部インスタンス変数にすればいいかと言えば、それは違う。
インスタンス変数を持つというのは、それぞれのオブジェクトがデータを持つということだ。
p
メソッドを使って、自分で作ったクラスのオブジェクトを一度見てみると面白い。
class User def initialize(name, age) @name = name @age = age end end user = User.new("Anna", 20) p user #=> #<User:0x00007f84b6832330 @name="Anna", @age=20>
こんな感じで、インスタンス(オブジェクト)自身がインスタンス変数を保持していることがわかる。プログラムの意図と、各インスタンス自身が保持すべきデータかどうかを踏まえた上で、インスタンス変数にすべきかどうか考える必要がある。
属性とは
属性(ぞくせい)とは、英語でattribute(アトリビュート)といい、モノの特徴や性質という意味だ。
インスタンスメソッドを読み書きするためのアクセサメソッドは、頭にattr
がついているが、このattr
はattributeのことだろう。
attr_reader :name, :age