自己紹介
はじめまして、はると申します。完全異業種からのエンジニア転職を目指して学習をしています。
概要
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 end
filling
の部分は、この時指定した名前になります。
本来はinitialize
で定義したインスタンス変数名と違う名前にすることはないと思いますが、
どこで命名したものが、どこに使われるのか確認するため、あえてattr_writer :croissant
と書いた場合には、下記のようなcroissant=(val)
メソッドとなります🥐def croissant=(val) @croissant = val end
-
p bun1.filling
実行時
attr_reader
により生成されたfilling
メソッドが呼び出されます。
そして@filling
が取得されます。
先ほど@filling
の中に、'鶏肉'
を再代入していたため、'鶏肉'
が表示されます。
下記のメソッドが、attr_reader :filling
の1行で表現されているということです。def filling @filling end
こちらも先ほどと同様、本来
attr_writer
やinitialize
と違う名前にすることはないと思いますが、
attr_reader :filling
の部分で命名した名前のメソッドとなります。
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