Зачем использовать Ruby's attr_accessor, attr_reader и attr_writer? - PullRequest
499 голосов
/ 19 февраля 2011

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

attr_accessor :var
attr_reader :var
attr_writer :var

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

Ответы [ 5 ]

729 голосов
/ 19 февраля 2011

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

23 голосов
/ 27 апреля 2011

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

15 голосов
/ 19 февраля 2011

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

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

13 голосов
/ 19 февраля 2011

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

11 голосов
/ 02 июля 2015

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...