Различие между классами и объектами Ruby


Углубляясь в фундаментальные различия

Язык программирования Ruby позволяет расширять и изменять реализацию любого класса или объекта.

Каждый объект — это класс, а каждый класс — это объект

Важно понять почти дзенскую реализацию языка Ruby: «Каждый класс — это объект, а каждый объект — это класс».

Почему это важно? Потому что то, что вы можете сделать с объектом, вы можете сделать с классом, и наоборот.

В следующем примере я использую синтаксис IRB.
Строка, начинающаяся с >, является подсказкой. Строка, начинающаяся с => — это результат оценки подсказанной строки.
Определим класс Book; ничего особенного.

> class Book; end
=> nil
Вход в полноэкранный режим Выход из полноэкранного режима

Создадим экземпляр Book, вызвав метод Book.new. Назовем этот экземпляр dune.

> dune = Book.new
=> #<Book:0x0000000106b56ce0>
Вход в полноэкранный режим Выход из полноэкранного режима

Рассмотрим методы «класса» экземпляра dune:

> dune.methods.grep(/class/)
=> [:singleton_class, :class]
Войти в полноэкранный режим Выход из полноэкранного режима

Метод methods

Метод class для dune возвращает книгу:

> dune.class == Book
=> true
Войти в полноэкранный режим Выход из полноэкранного режима

Что насчет singleton_class? Это не Book, это eigenclass этого экземпляра.

> dune.singleton_class
=> #<Class:#<Book:0x00000001006567a0>>
> dune.singleton_class == dune.class
=> false
Вход в полноэкранный режим Выход из полноэкранного режима

Почему важны singleton_class и class? Это концептуальная основа вызовов методов include и extend.

Для полноты картины, давайте рассмотрим метод класса Book.

> Book.methods.grep(/class/)
=>
[:superclass,
 :subclasses,
 :class_variable_set,
 :class_variables,
 :remove_class_variable,
 :class_variable_get,
 :class_variable_defined?,
 :singleton_class?,
 :class_exec,
 :class_eval,
 :public_class_method,
 :private_class_method,
 :singleton_class,
 :class]
Вход в полноэкранный режим Выход из полноэкранного режима

Существует больше методов класса Book, чем методов класса для экземпляра Book. Давайте рассмотрим методы singleton_class и class для Book.

> Book.class
=> Class

> Book.singleton_class
=> #<Class:Book>
Вход в полноэкранный режим Выход из полноэкранного режима

В этот момент сделайте паузу. Выпейте воды. Не знаю, много ли это было (или нет), но концептуальная разница между классом Book и экземпляром Book очень важна.

Расширение против включения

Давайте рассмотрим наш пример с книгой.

Сначала мы изучим Include

module Published
  # All book shall be published!
  def published?
    true
  end
end
Вход в полноэкранный режим Выход из полноэкранного режима

Сначала рассмотрим поведение include; оно добавляет методы к экземплярам класса.

class Book
  include Published
end
Войти в полноэкранный режим Выход из полноэкранного режима

Все экземпляры класса Book теперь будут иметь метод published?.

> Book.new.published?
=> true
Вход в полноэкранный режим Выход из полноэкранного режима

Но у класса Book не будет метода published?.

> Book.published?
=> undefined method `published?` for Book:Class (NoMethodError)
Войдите в полноэкранный режим Выход из полноэкранного режима

На самом деле, вы можете использовать методы интроспекции methods и instance_methods, чтобы проверить, какие методы определены на классе и экземплярах класса.

> Book.methods.include?(:published?)
=> false

> Book.instance_methods.include?(:published?)
=> true
Вход в полноэкранный режим Выход из полноэкранного режима

Затем мы исследуем Extend

Поведение extend добавляет методы к классу.

Давайте создадим новый модуль.

module Exciting
  def exciting?
    true
  end
end
Вход в полноэкранный режим Выход из полноэкранного режима

Пусть класс Book расширяет модуль Exciting.

> Book.extend Exciting
=> Book
end
Войдите в полноэкранный режим Выход из полноэкранного режима

Теперь класс Book имеет метод class exciting?.

> Book.exciting?
=> true
Вход в полноэкранный режим Выход из полноэкранного режима

Но экземпляр Book не имеет.

> Book.new.exciting?
=> undefined method `exciting?' for #<Book:0x0000000104441470> (NoMethodError)
Войдите в полноэкранный режим Выйти из полноэкранного режима

Чтобы запомнить вышесказанное, запомните: include — для экземпляров (а extend — для классов).

Давайте расширим класс синглтон объекта

Используя вышеприведенные модули Book и Exciting, давайте вернемся к тому, что каждый объект — это класс.

> left_hand_of_darknes = Book.new
=> #<Book:0x000000010523ccc8>
> left_hand_of_darkness.extend Exciting
=> #<Book:0x000000010523ccc8>
> left_hand_of_darkness.exciting?
=> true
> Book.new.exciting?
=> undefined method `exciting?' for #<Book:0x00000001008dc510> (NoMethodError)
Вход в полноэкранный режим Выход из полноэкранного режима

Выше мы расширяем left_hand_of_darkness экземпляр singleton_class модулем Exciting. И для этого экземпляра у этого объекта есть метод exciting?. Но другие экземпляры Book по-прежнему не имеют метода exciting?.

Итак, следует ли вам «расширить» экземпляр? Скорее всего, нет, но за годы работы с Ruby я нашел два случая, когда это был правильный подход. Я не помню их сейчас, но помню, что это элегантно и легко решало проблему.

Заключение

Этот экскурс в разницу между классом и экземпляром помогает заложить концептуальные основы объектной модели Ruby. Мое намерение в этом, возможно, эзотерическом обходе — подчеркнуть гибкость Ruby.

В следующих статьях я буду развивать эти концепции.

Оцените статью
devanswers.ru
Добавить комментарий