Singleton class (eigenclass) у Ruby: що це і навіщо потрібно

Обкладинка допису: Singleton class (eigenclass) у Ruby: що це і навіщо потрібно
ЗмістНатисність на посилання, щоб перейти до потрібного місця
У 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

Цей допис поки що не має жодних доповнень від автора/ки.

Як працюють масиви в Ruby: практичні приклади each, map, select, inject, reduce, filter_map
28 жовт., 10:38

Як працюють масиви в Ruby: практичні приклади each, map, select, inject, reduce, filter_map

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Як працює self, protected і private (Ruby)
28 жовт., 13:52

Як працює self, protected і private (Ruby)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Цілочисельне ділення у Ruby: чому 6 / 4 дорівнює 1
28 жовт., 14:10

Цілочисельне ділення у Ruby: чому 6 / 4 дорівнює 1

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
28 жовт., 14:42

Як працює &:to_s у Ruby і що таке Symbol#to_proc

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Що таке Proc і Lambda в Ruby?
28 жовт., 15:57

Що таке Proc і Lambda в Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Що відбувається, якщо викликати [1, 2, 3].map(&Person)
29 жовт., 17:54

Що відбувається, якщо викликати [1, 2, 3].map(&Person)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
==, equal?, eql?, === у Ruby: що перевіряють і коли використовувати
29 жовт., 20:47

==, equal?, eql?, === у Ruby: що перевіряють і коли використовувати

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Include, Extend, Prepend у Ruby: як вони працюють і в чому різниця
29 жовт., 21:20

Include, Extend, Prepend у Ruby: як вони працюють і в чому різниця

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
module_function у Ruby: коли методи модуля доступні як модульні та як функції
29 жовт., 21:53

module_function у Ruby: коли методи модуля доступні як модульні та як функції

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Що таке memoization в Ruby?
30 жовт., 10:17

Що таке memoization в Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
is_a?, kind_of?, instance_of? — як Ruby перевіряє тип об’єкта?
30 жовт., 19:55

is_a?, kind_of?, instance_of? — як Ruby перевіряє тип об’єкта?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
&& vs and — різниця в Ruby, яка може зламати ваш код
30 жовт., 20:23

&& vs and — різниця в Ruby, яка може зламати ваш код

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska