Progateでrubyを学んだ初心者はインスタンス変数の理解でつまづく

私はProgateでプログラミングの勉強を始めた初心者です🔰
なのでProgateで学んだことを基準として考えていたのですが、
**それが先入観となり理解に苦しんだ場面がありました。**

自分が参加しているオンラインサロンで質問をした際、
自分が質問をする前にも後にも、同じ質問をしている方がいました。
その方々もProgateで学んだようです。
これは**Progateで学んだ人がつまづきやすい点なのではないか**と思い、記事にしてみました！


#initializeメソッドで"self.変数名を使う"と思い込んでいる

例えば
**Movieというクラスを作り**
**movie1というインスタンスを生成**
**映画のタイトル(となりのトトロ)を表示させる**
というプログラムを作るとします。

Progateで学んだ内容に沿って書くと

```ruby
class Movie
  attr_accessor :name

  def initialize(name)
    self.name = name
  end

end

movie1 = Movie.new("となりのトトロ")
puts movie1.name
```
こう書く人が多いのではないでしょうか。
私はそうでした！

しかし、サロンでの皆さんのコードを見ている限り

```ruby
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を使わず書いていきます。

```ruby
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ではインスタンス変数の値はクラス内からしか取得出来ません。`
>そこで、それを可能にする為に`クラス内にインスタンス変数を参照する専用のメソッド「ゲッター」`を定義しなくてはいけません。

この説明を読んでも、正直わかってませんでした笑
とりあえず、ゲッターというものを付け加えてみました。

```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はこのゲッターを呼び出していたようなものです。

ゲッターが`インスタンス変数を参照するメソッド`に対し、
セッターは`インスタンス変数を更新するメソッド`です。

#インスタンス変数を更新するメソッド "セッター"

インスタンス変数の参照と同様に、`インスタンス変数の更新もクラス内でしかできません。`
そこで、それを可能にする為に`クラス内にインスタンス変数の内容を更新する専用のメソッド「セッター」`を定義します。

```ruby
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ではメソッドの定義の仕方を

```ruby
 def メソッド名(引数)
   処理
 end
```

って覚えましたよね？っとことは…

ここでの変数名は "name=　"ってこと！？
この**イコールってなに？泣**

今回の例での場合、

```ruby
def changename=(name)
```
ここです。**なにこれヤバすぎ、意味わからなさすぎ**
ゲッターを追加した際のコードの部分まで戻って、最後の2行を見てください。

```ruby
movie1.changename = "もののけ姫"
puts movie1.getname
```
movie1.changenameにもののけ姫を代入してるみたいに見えますよね？
違います！
実はこれ…

```ruby
movie1.changename=("もののけ姫")
puts movie1.getname
```
と書いても同じように出力されます！

つまり、
**`chagename=というメソッドに、もののけ姫という値を引数にして渡している`**のです！！
まるで代入されたかのような書き方。。
メソッド名に=を使い、それを呼び出す場合はスペースを空けて書いても同じ結果になるんです。


※なぜメソッド名に=を使うのか？という疑問も生まれましたが、
セッターはそう定義されることでセッターとしての役割を与えられるのです！
そう決められた書き方なんだなぁ、と思えばいいかと。

ここまで理解出来たところで、アクセサについて説明していきます。

#アクセサ
クラスを作ってすぐ下に書いたアレ

```ruby
attr_accessor :name
```
この1文がどんな役割をしているのかを理解することが重要です！
これって実は

```ruby
attr_areader :name
attr_writer :name 
```
この2行文の役割を上の1行で実行しています。

これは**アクセサ**と呼ばれるもので、
`attr_reader :変数名`はゲッターメソッドを
`attr_writer :変数名`はセッターメソッドを呼び出しています。
`attr_accessor :変数名`はゲッター・セッターメソッドどちらも１度に呼び出しています。

どういうことかというと…

```ruby
attr_reader :name
```
と書くことで

```ruby
  def getname #ゲッター
    @name
  end
```
が呼び出されている。
つまり
`attr_reader :name`を記述すれば、ゲッターメソッドを書かなくてもいいということ！

同様に

```ruby
attr_writer :name
```
は

```ruby
  def changename=(name) # セッター
    @name = name
  end
```
を呼び出しているので、
`attr_writer :name`を書けば、セッターメソッドも書かなくていい！


ということは…？？
`attr_reader`と`attr_writer`この2つの役割を1つにまとめた

```ruby
attr_accessor :name 
```
この1行を書くことによってゲッターとセッターどちらのメソッドの記入も省けます！


```ruby
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
```
は

```ruby
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】「ゲッター」と「セッター」を理解する](https://qiita.com/k-penguin-sato/items/5b75be386be4c55e3abf)