Just do IT

思うは招く

Rubyのattr_accessorとは一体何なのか

問題

Rubyを勉強していると頻繁にエンカウントするこんなやつ。

attr_accessor :hoge
attr_reader :hoge
attr_writer :hoge
  • メソッドがやっていることはなんとなくわかるが、いまいちしっくりこない
  • なんだかモヤモヤする

という感情が湧いたので、言語化しておく。

結論

  • attr_accessorは、インスタンス変数を参照したり、代入できるようにするメソッド
  • attr_accessorは、attr_readerとattr_writerを組み合わせた書き方
  • attr_accessor = attr_reader + attr_writer

解説

attr_reader

attr_readerから見ていく。

attr_readerは、引数に定義した変数を読み取れるようにするメソッド。 これはコードを見たほうが理解が早い。

まず、attr_readerを使わない書き方から。

class Test
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

test = Test.new("シュワちゃん")
puts test.name
  • initializeの引数にnameをとり、それをインスタンス変数の@nameに代入する
  • nameメソッドを作り、@nameを参照できるようにしている

ターミナルで実行すると。

$ ruby test.rb 
シュワちゃん

となる。 attr_readerを使うと、次のように書ける。

class Test
  attr_reader :name

  def initialize(name)
    @name = name
  end

  # これはいらない
  # def name
  #   @name
  # end
end

attr_readerは、実際には以下のコードと同じことを表現している。

  def name
    @name
  end

attr_writer

attr_writerは、引数に定義した変数に書き込みができるようになるメソッド。

attr_writerについても、まずはこれを使わずに書いてみる。

class Test
  attr_reader :name

  def initialize(name)
    @name = name
  end

  # 新しく追加
  def name=(new_name)
    @name = new_name
  end
end

test = Test.new("シュワちゃん")
puts test.name
test.name = "シルベスター"
puts test.name

新しく追加したのは以下の部分。

  def name=(new_name)
    @name = new_name
  end

@nameに外部から新しい名前new_nameを代入できるようにしている。

よって、もともと「シュワちゃん」という文字列が代入されていた@nameに対し、シルベスターを入れてみる。

test.name = "シルベスター"

これをターミナルで実行すると。

$ ruby test.rb 
シュワちゃん
シルベスター

インスタンス変数@nameの値が変更されている。

では、 attr_writerを使ってみよう。

class Test
  attr_reader :name
  attr_writer :name

  def initialize(name)
    @name = name
  end
  
  # 削除
  # def name=(new_name)
  #   @name = new_name
  # end
end

test = Test.new("シュワちゃん")
puts test.name
test.name = "シルベスター"
puts test.name

attr_writer :nameを書くだけで、さきほどと同じような結果が得られる。

$ ruby test.rb 
シュワちゃん
シルベスター

attr_accessor

そしてattr_accessorのお出番。

attr_readerとattr_writerはよく使われる。

「面倒だからattr_accessorにまとめようぜ?」と言って決めたかどうかはわからないが、attr_accessorを書くと参照と代入ができるようになる。

インスタンス変数 name に対する読み取りメソッドと書き込みメソッドの両方を定義します。 Module#attr_accessor (Ruby 2.7.0 リファレンスマニュアル)

class Test
  # 削除
  # attr_reader :name
  # attr_writer :name

  # 追加
  attr_accessor :name

  def initialize(name)
    @name = name
  end
end


test = Test.new("シュワちゃん")
puts test.name
test.name = "シルベスター"
puts test.name

実行。

$ ruby test.rb 
シュワちゃん
シルベスター

結果は変わらず。

最終的なコードはこちら。

class Test
  attr_accessor :name

  def initialize(name)
    @name = name
  end
end

短くなった。

これでようやくモヤモヤが解けたかな。