Змінна інстанція: self vs @


179

Ось код:

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методі.

Відповіді:


260

Запис @ageбезпосередньо отримує доступ до змінної екземпляра @age. Написання self.ageвказує об'єкту відправити собі повідомлення age, яке зазвичай повертає змінну екземпляра @age- але може робити будь-яку кількість інших речей залежно від ageспособу реалізації в заданому підкласі. Наприклад, у вас може бути клас MiddleAgedSocialite, який завжди повідомляє про свій вік на 10 років молодший, ніж він є насправді. Або, практично, клас PersistentPerson може ліниво читати ці дані з постійного магазину, кешувати всі його стійкі дані в хеші.


2
Я колись читав книгу в рейках і не розумію різниці між цим "я" і "@", тому мені завжди слід використовувати self.var_name у своїх методах (що не встановлює і не отримує), щоб зробити свої дані за допомогою загальнодоступного інтерфейсу, я витратив час, визначаючи це в геттері та сетері, правда?
sarunw

1
... англійська ... що ви маєте на увазі під будь-якою кількістю речей. Я не отримав останні два приклади.
користувач2167582

23

Різниця полягає в тому, що вона ізолює використання методу від його реалізації. Якщо реалізація властивості повинна змінитися - скажімо, зберегти дата народження, а потім обчислити вік, виходячи з різниці в часі між датою і датою народження - тоді код залежно від методу не потрібно змінювати. Якщо він використовував властивість безпосередньо, то зміна повинна поширюватися на інші області коду. У цьому сенсі використання властивості безпосередньо крихке, ніж використання наданого класом інтерфейсу.


15
Ох, адже self.age може посилатися на змінну екземпляра або метод екземпляра?
Нолан Емі

@. @ ... сумно це так
cyc115

7

Будьте попереджені, коли ви успадковуєте клас, з 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

Дякую за приклад @Prosseek, я зараз вивчаю Ruby on Rails, і саме така поведінка змушує мене відчувати, що Рубі надмірно складний>. <.
cyc115

3

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

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

8
Цей приклад зробив речі більш заплутаними.
Оскар Холмкрац

1
Мені шкода, але приклад для мене недостатньо коментований. Я не можу дотримуватися ваших міркувань.
kouty

Хтось, хто прийшов з Smalltalk, скаже, що об’єкт "надсилає повідомлення самому собі". Хтось, хто прийшов з Python, скаже, що об’єкт "викликає метод на себе". Не плутати; вони точно те саме. (Пурист семантики може заперечити, що вони однакові лише для мов з динамічним набором тексту і що виклик віртуального методу C ++ не є таким же, як відправлення повідомлення. Пурист правильний, але це, мабуть, виходить за рамки цього питання / відповідь.)
GrandOpener

Мені подобається приклад, але будь ласка, надайте ще кілька коментарів щодо того, що відбувається насправді. Важко дотримуватися без пояснень
CalamityAdam

2

Немає різниці. Я підозрюю, що це було зроблено саме для документальної цінності бачити self.ageі other_person.ageпоруч один з одним.

Я припускаю, що використання дозволяє в майбутньому записати фактичний getter, що може зробити щось складніше, ніж просто повернути змінну екземпляра, і в цьому випадку метод не потребуватиме змін.

Але це навряд чи турбуватиметься абстракція, адже якщо реалізація об'єкта змінилася, розумно змінити інші методи, в якийсь момент просте посилання всередині самого об’єкта цілком розумне.

У будь-якому випадку, абстракція ageвластивості все ще не пояснює явного використання цього виду self, оскільки просто звичайна ageтакож викликала б доступ.


-3

@age - це, безумовно, мінливий вік примірника

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.