私はProgateでプログラミングの勉強を始めた初心者です🔰
なのでProgateで学んだことを基準として考えていたのですが、
それが先入観となり理解に苦しんだ場面がありました。
自分が参加しているオンラインサロンで質問をした際、
自分が質問をする前にも後にも、同じ質問をしている方がいました。
その方々もProgateで学んだようです。
これはProgateで学んだ人がつまづきやすい点なのではないかと思い、記事にしてみました!
#initializeメソッドで"self.変数名を使う"と思い込んでいる
例えば
Movieというクラスを作り
movie1というインスタンスを生成
映画のタイトル(となりのトトロ)を表示させる
というプログラムを作るとします。
Progateで学んだ内容に沿って書くと
class Movie
attr_accessor :name
def initialize(name)
self.name = name
end
end
movie1 = Movie.new("となりのトトロ")
puts movie1.name
こう書く人が多いのではないでしょうか。
私はそうでした!
しかし、サロンでの皆さんのコードを見ている限り
class Movie
attr_accessor :name
def initialize(name)
@name = name
end
end
movie1 = Movie.new("となりのトトロ")
puts movie1.name
と書く人が多いです。
Progate出身者を悩ませているものは
initializeメソッド内の@です!!!
Progateではinitializeメソッドでself.変数名しか使わなかったのに、
@が出てきた、これはなんだろう…という疑問が生まれるのです。
#インスタンス変数を理解する
###インスタンス変数とは?
インスタンス毎に独立して値を持つことができる変数で
@変数名と書いて定義をするもの。
とあるが、Progateの中では@変数名と表記した変数を目にすることはなく、
インスタンス変数を扱うことのできるattr_accessorとselfを使っていました。
理解した今だからわかるのですが、
attr_accessorとselfがどのようにしてインスタンス変数を扱っているのかがわからないと、
@変数名が出てきたときに意味がわからなくなるのです。
attr_accessorを使わないで書いてみて?と言われたら、
Progateで学んだだけの人はおそらく書けない人が多いでしょう。
ということで、1から仕組みを理解していきましょう!
#initializeメソッドは初期化を行っている
attr_accessorを使わず書いていきます。
class Movie
def initialize(name)
@name = name
end
movie1 = Movie.new("となりのトトロ")
puts movie1.name
initializeメソッドは、「クラス名.new」でインスタンスを生成した直後に自動で呼び出されます。
とありますが、そのinitializeメソッド内では初期化
が行われています。
initializeメソッドではname
が引数とされ、
@name
というインスタンス変数にname
が代入される、これを初期の状態としています。
必ず初期化しなくてはいけない訳ではないのですが、後から初期化と同じ処理をしようとすると、処理を忘れてバグが発生したり、記述が煩雑になったりするため、
それを防ぐために、initialize メソッドで初期化を行うの良いとされています。
movie1というインスタンスを生成、引数nameにはとなりのトトロを入れて表示させようとすると、エラーが発生します。
これは**クラスの外からインスタンス変数の値を取得しようとしたため
**です。
どうしたらとなりのトトロを表示させることができるのか?
ここでゲッターやセッターが出てきます。
Progate学習者である私はこのゲッター・セッターも初耳でした。
#インスタンス変数を参照するメソッド "ゲッター"
本来
Rubyではインスタンス変数の値はクラス内からしか取得出来ません。
そこで、それを可能にする為にクラス内にインスタンス変数を参照する専用のメソッド「ゲッター」
を定義しなくてはいけません。
この説明を読んでも、正直わかってませんでした笑
とりあえず、ゲッターというものを付け加えてみました。
class Movie
def initialize(name)
@name = name
end
def getname # このメソッドがゲッター
@name
end
end
movie1 = Movie.new("となりのトトロ")
puts movie1.getname
getnameというメソッド、これがゲッターです。
このメソッドはクラスの中でインスタンス変数を参照されるように定義され、
それをクラスの外で呼び出すことによって表示させていたのです!
これでとなりのトトロは表示されるようになりました!
ちなみに、self.nameはインスタンスメソッドnameを呼び出すためのものなので、
self.nameはこのゲッターを呼び出していたようなものです。
ゲッターがインスタンス変数を参照するメソッド
に対し、
セッターはインスタンス変数を更新するメソッド
です。
#インスタンス変数を更新するメソッド "セッター"
インスタンス変数の参照と同様に、インスタンス変数の更新もクラス内でしかできません。
そこで、それを可能にする為にクラス内にインスタンス変数の内容を更新する専用のメソッド「セッター」
を定義します。
class Movie
def initialize(name)
@name = name
end
def getname #ゲッター
@name
end
def changename=(name) # セッター
@name = name
end
end
movie1 = Movie.new("となりのトトロ")
puts movie1.getname
movie1.changename = "もののけ姫"
puts movie1.getname
これでとなりのトトロを表示させ、
値をもののけ姫に更新して表示させることは出来ます。
が!
ここでまたProgate出身者を悩ますものが、、
###変数名に"="使ってる!?
Progateではメソッドの定義の仕方を
def メソッド名(引数)
処理
end
って覚えましたよね?っとことは…
ここでの変数名は "name= "ってこと!?
このイコールってなに?泣
今回の例での場合、
def changename=(name)
ここです。なにこれヤバすぎ、意味わからなさすぎ
ゲッターを追加した際のコードの部分まで戻って、最後の2行を見てください。
movie1.changename = "もののけ姫"
puts movie1.getname
movie1.changenameにもののけ姫を代入してるみたいに見えますよね?
違います!
実はこれ…
movie1.changename=("もののけ姫")
puts movie1.getname
と書いても同じように出力されます!
つまり、
**chagename=というメソッドに、もののけ姫という値を引数にして渡している
**のです!!
まるで代入されたかのような書き方。。
メソッド名に=を使い、それを呼び出す場合はスペースを空けて書いても同じ結果になるんです。
※なぜメソッド名に=を使うのか?という疑問も生まれましたが、
セッターはそう定義されることでセッターとしての役割を与えられるのです!
そう決められた書き方なんだなぁ、と思えばいいかと。
ここまで理解出来たところで、アクセサについて説明していきます。
#アクセサ
クラスを作ってすぐ下に書いたアレ
attr_accessor :name
この1文がどんな役割をしているのかを理解することが重要です!
これって実は
attr_areader :name
attr_writer :name
この2行文の役割を上の1行で実行しています。
これはアクセサと呼ばれるもので、
attr_reader :変数名
はゲッターメソッドを
attr_writer :変数名
はセッターメソッドを呼び出しています。
attr_accessor :変数名
はゲッター・セッターメソッドどちらも1度に呼び出しています。
どういうことかというと…
attr_reader :name
と書くことで
def getname #ゲッター
@name
end
が呼び出されている。
つまり
attr_reader :name
を記述すれば、ゲッターメソッドを書かなくてもいいということ!
同様に
attr_writer :name
は
def changename=(name) # セッター
@name = name
end
を呼び出しているので、
attr_writer :name
を書けば、セッターメソッドも書かなくていい!
ということは…??
attr_reader
とattr_writer
この2つの役割を1つにまとめた
attr_accessor :name
この1行を書くことによってゲッターとセッターどちらのメソッドの記入も省けます!
class Movie
def initialize(name)
@name = name
end
def name #ゲッター
@name
end
def name=(name) # セッター
@name = name
end
end
movie1 = Movie.new("となりのトトロ")
puts movie1.name
movie1.name = "もののけ姫"
puts movie1.name
は
class Movie
attr_accessor :name
def initialize(name)
@name = name
end
end
movie1 = Movie.new("となりのトトロ")
puts movie1.name
movie1.name = "もののけ姫"
puts movie1.name
と書くことができ、
クラス内に書いていたゲッター・セッターメソッドがattr_accessorというアクセサによって呼び出されることにより、同じ出力を可能にしているのです!
attr_accessorってこんなに便利なものだってことをわからずに使っていました。
#まとめ
まずなぜインスタンス変数を理解するのにつまづいたのか
- インスタンス変数は@変数名と書くものだと思っていなかったから
- イコールを使った変数名なんてあると思っていなかったから
- attr_accessorが何をしているものなのか理解していなかったから
ですね。
- インスタンス変数は@変数名と書いて定義する
- initializeメソッドは初期化をしている
- ゲッターというインスタンス変数を参照するメソッドがある
- セッターというインスタンス変数を更新するメソッドがある
- その2つのメソッドを使ってクラスの外からインスタンス変数の値を取得することができる。
- attr_accessorを使えばゲッター・セッターどちらも一度に呼び出すことができる。
Progateで学んだことは基本中の基本で、こうやってまた別の学習で知らないことを調べていくのって
理解がさらに深まるし、とても面白いことだなと思って日々学習しています。
批判している訳ではありませんが、ここはつまづきやすいなと思って書いてみました^^
参考と引用
【Ruby】「ゲッター」と「セッター」を理解する