ЗмістНатисність на посилання, щоб перейти до потрібного місця
У Ruby все - об’єкт (нуу майже все), навіть класи. І кожен об’єкт може мати власні унікальні методи, які не належать іншим екземплярам цього ж класу.
Що таке Proc і Лямбда читайте в моєму попередньому дописі.
Ці методи зберігаються не у звичайному класі, а у спеціальному прихованому класі, який називається singleton class або eigenclass.
Що таке singleton class?
Singleton class - це "особистий" клас конкретного об’єкта. Саме там Ruby зберігає методи, які визначені тільки для цього об’єкта.
user1 = "Alice"
user2 = "Bob"
def user1.greet
"Hello, I'm #{self}!"
end
p user1.greet # => "Hello, I'm Alice!"
p user2.greet # => NoMethodError
Тут greet - метод, який існує тільки для об’єкта user1. Ruby створив для user1 його власний singleton class і помістив туди цей метод.
Як побачити singleton_class?
Кожен об’єкт має метод singleton_class, який дозволяє звернутися до цього "прихованого" класу:
user1.singleton_class # => #<Class:#<String:0x0000000103c4b8f0>>
Якщо визначити метод прямо в цьому класі - він теж стане унікальним:
user = "Ruby"
user.singleton_class.class_eval do
def version
"3.2.1"
end
end
p user.version # => "3.2.1"
class << self
Коли ви пишете class << self, ви фактично відкриваєте singleton class поточного об’єкта. Це зручний синтаксис для визначення методів усередині нього.
Приклад 1: у звичайному об’єкті
user = "John"
class << user
def greet
"Hi, I'm #{self}"
end
end
p user.greet # => "Hi, I'm John"
Приклад 2: у класі (визначення методів класу)
class Person
class << self
def species
"Human"
end
end
end
p Person.species # => "Human"
Ruby просто відкрив singleton class для самого класу Person і додав туди метод species.
Якщо коротко:
- У Ruby кожен об’єкт має власний singleton class - прихований клас для його унікальних методів.
- Метод def obj.method або class << obj додає метод у singleton class цього об’єкта.
- Ви часто використовуєте це несвідомо, коли пишете методи класу (class << self) або унікальні методи для одного об’єкта.
Але для закріплення розглянемо питання. Чи можна викликати сінглтон метод класу на його об'єкті? НІ. Він не буде доступний на об’єкті класу (тобто на екземплярі).
У прикладі:
class Person
class << self
def species
"Human"
end
end
end
Ruby створює метод класу, тобто метод, який живе у singleton class самого класу Person, а не в Person’s instance methods.
Тому:
Person.species # => "Human" Person.new.species # => NoMethodError
Пояснення:
- Person - це об’єкт класу Class.
- class << self відкриває singleton class для цього об’єкта (Person).
- Метод species зберігається саме там.
- Екземпляри Person.new (створені об'єкти класу) його не бачать, бо шукають методи у звичайному класі Person, а не в його singleton class.
Якщо ж ти хочеш, щоб метод був доступний і як класовий, і як екземплярний, можна просто визначити його двічі або винести в module:
class Person
def self.species
"Human"
end
def species
"Human"
end
end
Person.species # => "Human"
Person.new.species # => "Human"
Або більш елегантно - через module:
module Species
def species
"Human"
end
end
class Person
extend Species # додає метод як класовий
include Species # додає метод як екземплярний
end
Цей допис поки що не має жодних доповнень від автора/ки.