6.3 アクセサ
オブジェクト指向型プログラミングで大切な考え方の一つにカプセル化がある。これはオブジェクトの状態を外部から隠し、不正な状態の変更によるバグを抑制するというものである。このため、Rubyではクラス内でインスタンス変数を定義しただけではオブジェクトの外からはその変数にアクセスする手段はない。
class Human
def initialize(name, age)
@name = name
@age = age
end
def to_s
"#{@name}(#{@age})"
end
end
例えばカプセル化がされていない場合、次のようなコードが意図せず実行可能になってしまう。
jhon = Human.new('Jhon', 18)
jhon.name = 'Pole'
p jhon.to_s
#=> "Pole(18)"
このように、"18歳のJhon"というデータの価値がなくなっておかしなオブジェクトができてしまう。デフォルトでインスタンス変数にアクセスできないことにすれば、意図的に参照できるようにしない限りこのようなことが起きることはない。
この例で@name、@ageに直接アクセスできるようにするためにはそれ用のメソッドを定義する必要がある。
class Human
def initialize(name, age)
@name = name
@age = age
end
def to_s
"#{@name}(#{@age})"
end
# ここから追加
def set_name(name)
@name = name
end
def get_name
@name
end
def set_age(age)
@age = age
end
def get_age
@age
end
# ここまで追加
end
jhon = Human.new('Jhon', 18)
p jhon.get_name
#=> "Jhon"
p jhon.set_name('Pole')
#=> "Pole"
p jhon.get_age
#=> 18
p jhon.get_name(19)
#=> 19
見て分かるように、各変数ごとに代入・参照メソッドを用意すると冗長なコードが並んだようなクラスになってしまう。この冗長なコードはアクセサメソッドを使って自動生成することで解決できる。
class Human
attr_accessor :name, :age # 追加
def initialize(name, age)
@name = name
@age = age
end
def to_s
"#{@name}(#{@age})"
end
end
jhon = Human.new('Jhon', 18)
p jhon.name
#=> "Jhon"
p jhon.name = "Pole"
#=> "Pole"
p jhon.age
#=> 18
p jhon.age = 19
#=> 19
attr_accessorは、渡したシンボル名のインスタンス変数の代入・参照メソッドを生成するメソッドである。アクセサメソッドには3種類あり、
- attr_accessor: 代入・参照メソッド
- attr_reader: 参照メソッドのみ
- attr_writer: 代入メソッドのみ
それぞれ、どこまでインスタンス変数へのアクセスを許すかによって使い分ける。