All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

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

Post cover: Singleton class (eigenclass) у Ruby: що це і навіщо потрібно
Table of contentsClick link to navigate to the desired location
This content has not been translated yet.We're showing the original Ukrainian content below.
У 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

This post doesn't have any additions from the author yet.

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

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

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

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

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

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

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
28 Oct 14:42

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

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

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

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

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

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

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

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

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

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

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

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

Що таке memoization в Ruby?

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

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

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

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

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