Переменная экземпляра: self vs @

Вот какой код:

class Person
  def initialize(age)
    @age = age
  end

  def age
    @age
  end

  def age_difference_with(other_person)
    (self.age - other_person.age).abs
  end

  protected :age
end

То, что я хочу знать, - это разница между использованием методов @age и self.age в age_difference_with.

Ответ 1

Запись @age позволяет получить доступ к переменной экземпляра @age. Написание self.age указывает объекту отправить сообщение age, которое обычно возвращает переменную экземпляра @age, но может делать любое количество других вещей в зависимости от того, как метод age реализован в данном подклассе. Например, у вас может быть класс MiddleAgedSocialite, который всегда сообщает о возрасте на 10 лет моложе, чем он есть на самом деле. Более того, класс PersistentPerson может лениво читать эти данные из постоянного хранилища, кэшировать все свои постоянные данные в хеше.

Ответ 2

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

Ответ 3

Предупредить, когда вы наследуете класс от Struct.new, который является аккуратным способом генерации intializer (Как генерировать инициализатор в Ruby?)

class Node < Struct.new(:value)
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value # or `p value`
    end
end 

n = Node.new(30)
n.show()

вернет

30
nil

Однако при удалении инициализатора он вернет

nil
30

С определением класса

class Node2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
    def show()
        p @value
        p self.value
    end
end

Вы должны предоставить конструктор.

n2 = Node2.new(30)
n2.show()

вернет

30
30

Ответ 4

Нет никакой разницы. Я подозреваю, что это было сделано только для документального значения просмотра self.age и other_person.age рядом друг с другом.

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

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

В любом случае абстракция свойства age по-прежнему не объясняет явное использование self, так как просто обычный age также вызвал бы аксессор.

Ответ 5

Первый ответ полностью правильный, но, как относительный новичок, мне не сразу стало ясно, что он подразумевал (отправляя сообщения самому себе... хм...). Я думаю, что короткий пример поможет:

class CrazyAccessors
  def bar=(val)
    @bar = val - 20 # sets @bar to (input - 20)
  end
  def bar
    @bar
  end

  def baz=(value)
    self.bar = value # goes through `bar=` method, so @bar = (50 - 20)
  end

  def quux=(value)
    @bar = value     # sets @bar directly to 50
  end
end

obj  = CrazyAccessors.new
obj.baz = 50
obj.bar  # => 30
obj.quux = 50
obj.bar  # => 50

Ответ 6

вызов self.value для получателя. Потому что, когда вы вызываете self.value в классе.

Ошибка: неопределенный метод 'value'. Я должен объявить геттер для класса

Ответ 7

@age - определенно возраст переменной экземпляра

self.age - относится к возрасту свойства экземпляра.