Rubyのクラスのアクセサについて

Rubyのクラスについて、インスタンス変数の参照・代入とそれを可能にするattr_accessorをまとめます。

クラスの基本内容については[先日まとめた記事](https://qiita.com/bellmmm/items/a1292d5143d0f128c009)をご確認ください。  
selfと@の違い、selfと()の省略については[こちらの記事](https://qiita.com/bellmmm/items/f309bcddb71db53da662)をご確認ください。
インスタンスメソッド、クラスメソッド、メソッドの呼び出し制限によるプライベートメソッドについては[こちらの記事](https://qiita.com/bellmmm/items/c06acf71aeed07b3cd7f)をご確認ください。

# インスタンス変数に対する読み取りと書き込み


## インスタンス変数は直接参照・代入できない
インスタンス変数は、クラスの中では参照(値を取り出す)・代入が可能ですが、クラスの外からは直接参照・代入はできません。  
例) インスタンス変数を直接参照・代入するとエラーになる
```ruby
class S

  def initialize(name="name")
    @name = name
  end

end

s=S.new("aaa")
puts s.@name  #=>syntax error, unexpected tIVAR
puts s.@name="bbb"  #=>syntax error, unexpected tIVAR
puts s.name  #=>NoMethodError
puts s.name="bbb"  #=>NoMethodError
```
そもそも上記のような`インスタンス名.メソッド名`で呼び出すためにはインスタンスメソッドを作成する必要があります。

## インスタンス変数を参照・代入するメソッドを作る

インスタンス変数を参照・代入するメソッドは以下のように呼ばれます。  

- ゲッタ：インスタンス変数の値を取り出すメソッド  
- セッタ：オブジェクトのインスタンス変数に値を代入するためのメソッド

例2)インスタンス変数を参照・代入するインスタンスメソッドを作る
```ruby
class S

  def initialize(name="name")
    @name = name
  end

  # ゲッタ
  def name
    @name
  end

  # セッタ
  def name=(string)
    @name = string
  end

end

s=S.new("aaa")
puts s.name  #=>aaa
puts s.name="bbb"  #=>bbb
```
ゲッタとセッタを定義することでインスタンスメソッドの参照・代入が可能になりました。しかしインスタンス変数が多くなると、インスタンス変数ごとにゲッタとセッタを定義するのは煩雑になります。  
そこで、attrで始まるメソッドを使用します。

## attrで始まるメソッド4つ
- attr_reader  
インスタンス変数の読み取りメソッド(ゲッタ)を定義するメソッドです。インスタンス変数の参照はできても代入はできません。
```ruby
class S
　attr_reader :name

　def initialize(name="name")
  　@name = name
　end
  
end
  
s=S.new("aaa")
puts s.name  #=>aaa
puts s.name="bbb"  #=>NoMethodError
```

詳しく言うと、`attr_reader`は上の`S`自体のインスタンスメソッドではなく、`Module`というクラスのインスタンスメソッドらしいです。
参考:[class Module (Ruby 3.1 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/class/Module.html)

- attr_writer  
インスタンス変数の書き込みメソッド(セッタ)を定義するメソッドです。インスタンス変数の参照はできませんが、代入はできます。

```ruby
class S
  attr_writer :name
  
  def initialize(name="name")
    @name = name
  end
  
end
    
s=S.new("aaa")
puts s.name  #=>NoMethodError
puts s.name="bbb"  #=>bbb
```

- attr  
第1引数に指定したインスタンス変数の読み取りのためのインスタンスメソッド(ゲッタ)を定義するメソッドです。第2引数にtrueを指定した場合には書き込みメソッド(セッタ)も同時に定義します。
```ruby
class S
  attr :name, true

  def initialize(name="name")
    @name = name
  end
  
end
  
s=S.new("aaa")
puts s.name  #=>aaa
puts s.name="bbb"  #=>bbb
```
```ruby
class S

  # 第一引数は複数指定できる。
  attr :name, :email
  def initialize(name="name", email="email")
    @name = name
    @email = email
  end

end

s=S.new("aaa", "aaa@email.com")
puts s.name  #=>aaa
puts s.email  #=>aaa@email.com
```
しかし、このattrメソッドは、第一引数を複数指定することは可能ですが、第一引数を複数指定しかつ第二引数にtrueを指定することができません。
```ruby
class S

  # 第一引数を複数指定し、第二引数をtrueにしてセッタも定義するよう指定するが、エラーになる
  attr :name, :email, true  #=>TypeError
  def initialize(name="name", email="email@email.com")
    @name = name
    @email = email
  end
  
end
```
- attr_accessor  
そこで、attr_accessorの出番です。  
attr_accessorはインスタンス変数に対する読み取りと書き込みのメソッド(ゲッタとセッタ)の両方を定義するインスタンスメソッドです。
attr_accessorはattr_readerとattr_writerをあわせたようなもの、もしくはattrの第1引数が複数のときも第2引数にtrueを指定できるものです。
```ruby
class S
  attr_accessor :name, :email 
  
  def initialize(name="name", email="email")
    @name = name
    @email = email
  end

end
      
s=S.new("aaa", "aaa@email.com")
puts s.name  #=>aaa
puts s.name="bbb"  #=>bbb
puts s.email  #=>aaa@email.com
puts s.email="example@email.com"  #=>example@email.com
```


# まとめ
rubyでよく使われるattr_accessorについて説明するために、attr_reader、attre_writer、attrについても言及しながらまとめました。attrについては調べて初めて存在を知りました。また、attr_accessorは特に意識せず便利機能のように使っていましたが、その裏でゲッタとセッタのメソッドが自動で定義され、クラスの外からインスタンス変数の参照や代入ができるようになるということが分かりました。コードを書く際に、その仕組みが何なのかを理解してから使うことを普段から意識したいと感じました。