Rubyでは、以下のようなキーを使ってインスタンス変数を共有する便利な方法があります。
attr_accessor :var
attr_reader :var
attr_writer :var
単純に attr_accessor
を使えるのに、なぜ attr_reader
や attr_writer
を選ぶのでしょうか?パフォーマンスのようなものがあるのでしょうか(疑わしいですが)。何か理由があるのでしょう、そうでなければこのようなキーは作られません。
アクセサを使い分けることで、コードを読む人に意図を伝えたり、パブリック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_...ヘルパーがなく、自分でアクセサを書かなければならないとしたら、自分のクラスが必要とする以上のアクセサを書きますか? 例えば、ageが読まれるだけでいいなら、それを書けるようにするメソッドも書きますか?
オブジェクトのすべての属性が、クラスの外から直接設定できるわけではありません。すべてのインスタンス変数にライターを持つことは、一般的にカプセル化が弱いことを示しており、クラス間の結合が強すぎることを警告しています。
実例を挙げてみましょう。私は、コンテナの中にアイテムを入れるデザインプログラムを書きました。アイテムは attr_reader :container
を持っていましたが、ライターを提供する意味はありませんでした。アイテムのコンテナが変わるのは、新しいコンテナに入れるときだけで、そのときには位置情報も必要になるからです。