【Ruby】attr_accessor・ゲッター・セッターについて掘り下げて考えた
自己紹介
はじめまして、はると申します。完全異業種からのエンジニア転職を目指して学習をしています。
概要
Rubyの attr_accessor について、わかりやすく解説されている記事はすでにたくさんありますが、自分が理解できるまで掘り下げたことをまとめました。
注意
私は前職が完全異業種であり、スクールに入って初めてプログラミングに触れました。
そんな自分が理解しづらかった部分を、同じように初めてプログラミングの概念に触れた人に向けてまとめました。
自分の復習も兼ねて、超超噛み砕いて書いているため、周りくどい書き方になっている箇所もあるかと思います🙇
ゲッターとセッターとは
こちらの記事が大変わかりやすかったので引用させていただきます🙇
こちらの記事で全て理解できた方は今回の記事は対象外となります。
一応、公式リファレンスからも引用すると、それぞれ下記のように解説されています。
attr_reader:インスタンス変数 name の読み取りメソッドを定義します。1)
attr_writer:インスタンス変数 name への 書き込みメソッド (name=) を定義します。2)
attr_accessor:インスタンス変数 name に対する 読み取りメソッドと書き込みメソッドの両方 を定義します。3)
噛み砕いて考える
ゲッターとセッターについてはなんとなくわかりましたが、以下の疑問が残りました。
- ゲッターを追加した後に、それを呼び出すときに使われているメソッド名はどの部分で決めた名前が使われている?
- ゲッターとセッターの解説内によく出てくる
initializeメソッドはなぜ必要? - 実行順はどうなっている?😵💫
この疑問を解決するため、どのような順番でコードが実行されているのか考えてみました。
下記のRubyのコードがあったとして、実行順に見てみます。
class MeatBun # 肉まんクラス
attr_reader :filling
attr_writer :filling
def initialize(filling)
@filling = filling # fillingは具
end
end
bun1 = MeatBun.new('豚肉')
bun1.filling = '鶏肉'
p bun1.filling
-
bun1 = MeatBun.new('豚肉')実行時
MeatBunクラスのinitializeメソッド(新しいインスタンスが作成されたときに自動的に呼び出されるメソッド)が呼び出されます。
initializeメソッドの引数として'豚肉'が渡されて、@fillingに'豚肉'が入っている状態でMeatBunのインスタンスが生成され、それがbun1に代入されています🐖
この段階ではattr_readerやattr_writerは介していません。 -
bun1.filling = '鶏肉'実行時
attr_writerにより生成されたfilling=(val)メソッドが呼び出されます。
元々'豚肉'が入っていた@fillingに、引数で渡した'鶏肉'を再代入します🐓
下記のメソッドが、attr_writer :fillingの1行で表現されているということです。
(valという言葉を使っているのは、公式リファレンスの通りにしました。)def filling=(val) @filling = val enddef croissant=(val) @croissant = val end -
p bun1.filling実行時
attr_readerにより生成されたfillingメソッドが呼び出されます。
そして@fillingが取得されます。
先ほど@fillingの中に、'鶏肉'を再代入していたため、'鶏肉'が表示されます。
下記のメソッドが、attr_reader :fillingの1行で表現されているということです。def filling @filling end
attr_accessorについて
上記のようにattr_readerとattr_writerを両方使う場合は、attr_accessorの1行で済みます。
class MeatBun
attr_accessor :filling # ここ
def initialize(filling)
@filling = filling
end
end
疑問は解決できたか?
-
bun1.filling = '鶏肉'で使われていた.fillingの部分は、attr_writerで生成されたfilling=(val)メソッドのことでした。 -
initializeメソッドがないとインスタンス生成時には@fillingが無いので、bun1 = MeatBun.new('豚肉')のように、初めから値を渡してインスタンス作成することはできません。
ですが、initializeが無くても、bun1 = MeatBun.newと引数無しでインスタンスを生成することはできます。attr_writerとattr_readerがあれば、引数無しでインスタンス生成後にbun1.filling = '鶏肉'やp bun1.fillingで値の代入や参照が可能です。 - 実行順番を整理したことで、
最初に@fillingへの値の代入を行っているのはinitializeメソッド、
値の更新を行っているのはattr_writerで定義されたメソッド、
値を参照しているのはattr_readerで定義されたメソッド、
とそれぞれ別々のメソッドで行われていることがわかりました。 - また、下記の3つのメソッドでは同じ名前のインスタンス変数
@fillingが使われていますが、同じクラス内の異なるメソッドで同じ名前のインスタンス変数を使用している場合、それらが同じものと見なされるということがわかりました。(だから値の更新や参照をしても同じ@fillingに対して行えます)class MeatBun def filling # attr_reader :filling @filling end def filling=(val) # attr_writer :filling @filling = val end def initialize(filling) @filling = filling end end
さいごに
細かく噛み砕いたことでずっと苦手意識を持っていたゲッターやセッターの概念、attr_accessorについての理解が進みました!
ここまで読んで頂きありがとうございました🙂
参考・引用記事
引用記事
1)Ruby 3.3 リファレンスマニュアル attr_reader
2)Ruby 3.3 リファレンスマニュアル attr_writer
3)Ruby 3.3 リファレンスマニュアル attr_accessor
Discussion