Rubyのattr_accessor、attr_reader、attr_writerを使用する理由


517

Rubyには、次のようなキーを使用してインスタンス変数を共有するこの便利で便利な方法があります。

attr_accessor :var
attr_reader :var
attr_writer :var

なぜ選択するのattr_readerattr_writer、または単に使用できるのattr_accessorか?パフォーマンスのようなものはありますか?理由があると思いますが、そうでなければ、そのようなキーを作成しなかったでしょう。


回答:


746

さまざまなアクセサーを使用して、コードを読んでいる誰かに意図を伝え、パブリックAPIがどのように呼び出されても正しく機能するクラスを簡単に記述できます。

class Person
  attr_accessor :age
  ...
end

ここでは、年齢の読み取りと書き込みの両方ができることがわかります。

class Person
  attr_reader :age
  ...
end

ここでは、年齢のみを読み取ることができることがわかります。このクラスのコンストラクターによって設定され、その後は一定のままであると想像してください。ageにミューテーター(ライター)が存在し、一度設定されたageが変更されないと仮定してクラスが作成された場合、そのミューテーターを呼び出すコードからバグが発生する可能性があります。

しかし、舞台裏で何が起こっているのでしょうか?

あなたが書く場合:

attr_writer :age

これは次のように変換されます。

def age=(value)
  @age = value
end

あなたが書く場合:

attr_reader :age

これは次のように変換されます。

def age
  @age
end

あなたが書く場合:

attr_accessor :age

これは次のように変換されます。

def age=(value)
  @age = value
end

def age
  @age
end

それを知って、これについて考える別の方法があります:attr _...ヘルパーがなく、自分でアクセサーを作成しなければならなかった場合、クラスに必要な数以上のアクセサーを作成しますか?たとえば、年齢を読み取るだけでよい場合、それを書き込むメソッドも作成しますか?


53
あり、大幅なパフォーマンス上の利点は、書き込みにattr_reader :adef a; return a; end confreaks.net/videos/...
Nitrodist

83
@Nitrodist、興味深い。Ruby 1.8.7の場合、attr_reader定義されたアクセサは、手動で定義されたアクセサが実行する時間の86%かかります。Ruby 1.9.0の場合、attr_reader定義されたアクセサーは、手動で定義されたアクセサーに比べて94%の時間がかかります。ただし、私のすべてのテストでは、アクセサーは高速です。アクセサーは約820ナノ秒(Ruby 1.8.7)または440ナノ秒(Ruby 1.9)かかります。これらの速度では、attr_accessor全体的なランタイムを1秒でも改善するというパフォーマンス上の利点を得るために、何億回もアクセサを呼び出す必要があります。
ウェインコンラッド

22
「おそらく、それはこのクラスのコンストラクターによって設定され、一定のままです。」それは正確ではありません。リーダーを持つインスタンス変数は頻繁に変更される可能性があります。ただし、それらの値はクラスによって個人的にのみ変更されることが意図されています。
mlibby 2012

11
あなたは、次のような2つの以上の属性を追加する「」使用することができますattr_accessor :a, :b
Andrew_1510

2
これらすべての年の後に価値があるもの:github.com/JuanitoFatas/…ruby 2.2.0の最新のベンチマークによると、attr_ *はゲッターとセッターよりも高速です。
molli 2017年

25

上記の答えはすべて正しいです。attr_readerそしてattr_writer、それらはそれらの短縮形であるメソッドを手動でタイプするよりも書く方が便利です。それとは別に、メソッド定義を自分で書くよりもはるかに優れたパフォーマンスを提供します。詳細については、アーロンパターソンによるこの講演PDF)のスライド152以降を参照してください。


16

オブジェクトのすべての属性がクラスの外部から直接設定されることを意図しているわけではありません。すべてのインスタンス変数にライターがあることは、一般的にカプセル化が弱い兆候であり、クラス間の結合が多すぎることを警告しています。

実用的な例として、アイテムをコンテナー内に配置する設計プログラムを作成しました。アイテムにはがattr_reader :containerありましたが、ライターを提供することは意味がありませんでした。アイテムのコンテナーを変更する必要があるのは、新しいコンテナーに配置するときだけであり、これには位置情報も必要になるためです。


16

アクセサーは変数へのアクセスを制限しますが、その内容は制限しないことを理解することが重要です。Rubyでは、他のオブジェクト指向言語と同様に、すべての変数がインスタンスへのポインターです。したがって、たとえばハッシュの属性があり、それを「読み取り専用」に設定した場合は、常にその内容を変更できますが、ポインターの内容は変更できません。これを見てください:

irb(main):024:0> class A
irb(main):025:1> attr_reader :a
irb(main):026:1> def initialize
irb(main):027:2> @a = {a:1, b:2}
irb(main):028:2> end
irb(main):029:1> end
=> :initialize
irb(main):030:0> a = A.new
=> #<A:0x007ffc5a10fe88 @a={:a=>1, :b=>2}>
irb(main):031:0> a.a
=> {:a=>1, :b=>2}
irb(main):032:0> a.a.delete(:b)
=> 2
irb(main):033:0> a.a
=> {:a=>1}
irb(main):034:0> a.a = {}
NoMethodError: undefined method `a=' for #<A:0x007ffc5a10fe88 @a={:a=>1}>
        from (irb):34
        from /usr/local/bin/irb:11:in `<main>'

ご覧のとおり、ハッシュ@aからキーと値のペアを削除したり、新しいキーを追加したり、値を変更したりすることができます。ただし、は読み取り専用のインスタンス変数であるため、新しいオブジェクトを指すことはできません。


13

クラスの外部からインスタンス変数に完全にアクセスできるようにしたくない場合があります。インスタンス変数への読み取りアクセスを許可することは理にかなっていますが、それに書き込むことは意味がない場合がたくさんあります(たとえば、読み取り専用ソースからデータを取得するモデル)。逆のことをしたい場合もありますが、頭のてっぺんからはずれないものは考えられません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.