RDRubyTutorial: Метапрограммный Ruby level 2
мая 9, 2012 | Published in Ruby, Основы | 6 Comments
Предварительно рекомендую прочесть Метапрограммный Ruby.
К моему удивлению не все Ruby программисты знают внутреннего устройства Ruby. Не в том, смысле, что Ruby’ст должен изучать исходники своего любимого языка программирования, но в том, что многие моменты не понятны. Например не понятно, чем класс отличается от объекта или что такое singleton’ы в Ruby. Эта статья призвана совершить некоторую демистификацию Ruby. В простой манере и без влезания в код самого Ruby я расскажу почему что-то работает именно так, как работает. Статья ориентирована не на новичков, а на программистов более-менее среднего уровня, то есть достаточно хорошо знакомых с Ruby.
Статья будет иметь формат повествования, она будет лишена структуры, читайте ее просто как программистскую сказку.
Классы — это объекты, но только на половину. Классы являются экземплярами одного из базовых классов Class, однако они не являются «классическими» объектами.
$ ruby -v
ruby 1.9.3p191 (2012-04-19 revision 35398) [i686-linux]
$ irb
class A end A.class #=> Class
В Ruby объекты не могут иметь собственных методов. Объект — это просто набор данных. Например объект класса Human содержит данные об имени, росте, весе, цвете глаз и прочих параметрах человека. В этом смысле объект в Ruby не сильно отличается от ассоциативного массива (а в JavaScript — это и вовсе одно и то же). Поскольку объект не может хранить собственные методы, то его методы хранятся в классе. То, что класс хранит методы своих экземпляров — это основное отличие класса от обыкновенного объекта. Даже не то, что класс может создавать экземпляры, а то, что он хранит методы экземпляров, ведь мы можем используя техники метапрограммирования расширить обыкновенный объект до того, что он будет подобным классу в своем поведении, но методы своих экземпляров он хранить не сможет.
class A def hello puts 'hello' end end a = A.new a.hello # hello
Когда мы для a вызываем метод hello, то, на самом деле a передает сообщение об его вызове по цепочке наследования, сначала к своему singleton классу, затем к классу A и т.д. пока метод не будет найден. У самого же a нет методов!
Вот он singleton класс:
a.singleton_class #=> #<Class:#<A:0x82bf770>> А вот еще один: A.singleton_class #=> #<Class:A>
Любой объект в том числе и класс (который объект только на половину) имеет закрепленный за собой singletion класс. Singleton класс встраивается в цепочку наследования. Здесь он наследуется от класса A:
a.singleton_class #=> #<Class:#<A:0x82bf770>>
А здесь он наследуется от класса Class:
A.singleton_class #=> #<Class:A>
Таким образом Singleton класс — это незаметная ссылка между экземпляром класса и классом. Когда мы определяем объектам специальные методы, например так:
def a.bye puts 'bye' end a.bye # bye
… или так:
def A.how_are_you puts 'How are you?' end A.how_are_you # How are you?
… или так:
class A def self.what_is_your_name puts "What is your name?" end end A.what_is_your_name # What is your name?
… или даже так:
class A class << self def how_old_are_you puts 'How old are you?' end end end A.how_old_are_you # How old are you?
… то мы не явно имеем дело с Singleton классами. Класс способен хранить методы своих экземпляров, но не свои собственные. Каждый класс имеет Singleton класс, который хранит уникальные методы класса. Стандартные методы класс, будучи экземпляром класса Class, хранит в классе Class и других базовых классах, например классе Object от которого он наследуется:
A.superclass #=> Object
Все уникальные методы объектов, которые вы определяете хранятся в Singleton классах, в универсальные методы хранятся в их классах.
a.singleton_methods #=> [:bye] A.singleton_methods #=> [:how_are_you, :what_is_your_name, :how_old_are_you] A.methods.count #=> 100 (A.methods - A.singleton_methods).count #=> 97
Почему название такое — Singleton класс? — Дело в том, что этот класс может иметь только один объект:
a.singleton_class.new TypeError: can't create instance of singleton class
Потому и название такое, хотя ме больше нравится название Eigenclass, которое, однако встречается значительно реже. С названием eigenclass не возникает путаницы так как существует еще такой паттерн как Singleton («Одиночка»), который позволяет создавать классы, которые не могут иметь более одного экземпляра. Из-за того, что Eigenclass имеет только экземпляр — объект, за которым он закреплен, то и было решено использовать название Singleton, которое правда не описывает его предназначение и вносит путаницу с паттерном проектирования о котором говорилось выше. Название Eigenclass лучше отображает суть ведь в переводе с английского/немецкого оно означает «собственный класс».
Делаем выводы:
- Объект, если рассматривать его совсем примитивно является просто хэшем, который хранит определенные данные.
- Класс является объектом, который способен хранить методы своих экземпляров.
- Singleton класс — это класс, экземпляром которого на самом деле является объект, который может обладать только одним экземпляром, и которых хранит все уникальные методы объекта.
- Мы не говорим, что a наследуется от A и не смотря на наличие singleton класса — это правильно, так как singleton класс — это просто реализация, но не парадигма и его наличие ни на что не влияет.
Люди часто не понимают, что делает следующий код:
class A class << self def how_old_are_you puts 'How old are you?' end end end
Этот код — это в некотором смысле синтаксический сахар. Выражение class нуждается в получении константы — имени класса. self — ссылка на текущий контекст, если мы не указываем приемник вызова метода явно, то предполагается, что им является self — объект в контексте которого осуществляется вызов. Когда мы используем class << self, то это просто специальный синтаксис, через стрелку «<<« в котором мы как бы открываем контекст предка — singleton класса. В class мы не можем передать что-то иное, кроме константы, в этом случае возникнет синтаксическая ошибка:
class A class singleton_class def rubydev puts "RubyDev.ru" end end end
SyntaxError: (irb):90: class/module name must be CONSTANT
from /home/vladimir/.rvm/rubies/ruby-1.9.3-head/bin/irb:16:in `<main>’
Singleton классы являются безымянными (анонимными). Им не нужно имя хотя бы потому, что они сами по себе существовать не могу, с ними на прямую никто работать не должен, а если и возникает такая необходимость, то обратиться к ним можно через метод singleton_class объекта, за которым они закреплены.
Синтаксис class << self просто обрабатывается иначе. Интерпретатор понимает его не как необходимость вызова для объекта class метода << и передачи аргумента self, а как нечто иное — переключение в контекст singleton класса. Вот и все!
мая 9, 2012 at 19:19 (#)
Примечательно, что именно сегодня вышла близкая статья на хабре об include и extend, которая также в конечном счете свелась к феномену singleton class.
Вот так за один день увидели свет две замечательные статьи, прочистившие голову от шелухи по сабжу. Спасибо.
мая 9, 2012 at 20:16 (#)
Свою статью я написал после участия в комментировании статьи на хабре и там возник вопрос по поводу class << self.
мая 17, 2012 at 01:48 (#)
Т.е. каждый экземпляр класса ссылается на свой единственный eigenclass ?
Было бы классно, если бы была илюстрация
мая 17, 2012 at 09:14 (#)
none, да, каждый класс является экземпляром не от класса Class, а от eigenclassa, который наследуется от Class, а точнее от родительского класса объекта.
мая 22, 2012 at 16:41 (#)
Singleton класс — это класс, экземпляром которого на самом деле является объект, который может обладать только одним экземпляром, и которых хранит все уникальные методы объекта.
Сложно воспринимаетмя это предлжение текста.
мая 22, 2012 at 16:56 (#)
*Сложно воспринимается это предложение.