Зачем использовать Ruby's attr_accessor, attr_reader и attr_writer?

В Ruby есть удобный и удобный способ обмена переменными экземпляра с помощью таких ключей, как

attr_accessor :var
attr_reader :var
attr_writer :var

Почему я бы выбрал attr_reader или же attr_writer если бы я мог просто использовать attr_accessor? Есть ли что-то вроде производительности (в чем я сомневаюсь)? Я думаю, что есть причина, иначе они не сделали бы такие ключи.

5 ответов

Решение

Вы можете использовать различные методы доступа, чтобы сообщить о своем намерении кому-то, читающему ваш код, и упростить написание классов, которые будут работать правильно, независимо от того, как вызывается их публичный API.

class Person
  attr_accessor :age
  ...
end

Здесь я вижу, что могу читать и писать возраст.

class Person
  attr_reader :age
  ...
end

Здесь я вижу, что могу читать только возраст. Представьте, что он установлен конструктором этого класса и после этого остается постоянным. Если для возраста существовал мутатор (писатель), а класс был написан, предполагая, что установленный возраст не изменится, то в результате вызова кода этого мутатора может возникнуть ошибка.

Но что происходит за кулисами?

Если вы напишите:

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_..., и вы должны были написать аксессоры самостоятельно, вы бы написали больше аксессоров, чем требовалось вашему классу? Например, если нужно только прочитать возраст, вы бы также написали метод, позволяющий записать его?

Все ответы выше верны; attr_reader а также attr_writer удобнее писать, чем вручную набирать методы, для которых они сокращены. Кроме того, они предлагают гораздо лучшую производительность, чем самостоятельное написание определения метода. Для получения дополнительной информации см. Слайд 152 из этого доклада ( PDF) Аарона Паттерсона.

Важно понимать, что методы доступа ограничивают доступ к переменным, но не к их содержимому. В ruby, как и в некоторых других языках OO, каждая переменная является указателем на экземпляр. Так, если у вас есть атрибут к Hash, например, и вы устанавливаете его как "только для чтения", вы всегда можете изменить его содержимое, но не содержимое указателя. Посмотри на это:

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>'

Как видите, можно удалить пару ключ / значение из Hash @a, добавить новые ключи, изменить значения и так далее. Но вы не можете указать на новый объект, потому что это переменная экземпляра только для чтения.

Не все атрибуты объекта предназначены для непосредственной установки вне класса. Наличие записи для всех переменных вашего экземпляра, как правило, является признаком слабой инкапсуляции и предупреждением о том, что вы вводите слишком большую связь между вашими классами.

В качестве практического примера: я написал дизайнерскую программу, в которой вы помещаете предметы в контейнеры. Предмет имел attr_reader :container, но предлагать писателя не имело смысла, так как контейнер элемента должен меняться только тогда, когда он помещается в новый, что также требует информации о расположении.

Вы не всегда хотите, чтобы переменные вашего экземпляра были полностью доступны извне класса. Есть много случаев, когда разрешение доступа на чтение к переменной экземпляра имеет смысл, но запись в него может не иметь места (например, модель, которая извлекает данные из источника только для чтения). Бывают случаи, когда вы хотите противоположного, но я не могу вспомнить ни одного, который не был придуман над моей головой.

Другие вопросы по тегам