(Впрочем, и связь с недавно вышедшей статьей Метаклассы в Objective-C отрицать не буду.)
Чтобы разобраться с понятием метаклассов нам понадобится всего два принципа, из которых мы сделаем (почти очевидные) логические выводы.
Первый принцип является основополагающим для объектного программирования в целом и, по идее, должен быть таковым для любого объектного языка (для Smalltalk в любом случае это так):
Всё является объектом, то есть может получать сообщения и реагировать на них.
Классы так же подпадают под это самое «всё». Например, мы порождаем объекты, посылая сообщения классу, экземпляр которого мы хотим создать:
point := Point x: 1 y: 2.
Никаких сверхъестественных спец-операций по чудесному созданию объектов, только посылка сообщений. В данном случае мы посылаем сообщение
#x:y:
классу Point
в надежде получить точку по двум координатам.Второй принцип не является основополагающим для объектного программирования, но в Smalltalk (как и в большинстве современных объектных языков), к сожалению, тоже был заложен:
Поведение объекта определяется его классом: в классе задается набор методов, описывающих реакцию экземпляров на то или иное сообщение.
Так, все методы для точек определены в классе
Point
— это понятно. Мы можем узнать класс объекта, послав ему сообщение #class
.point class."-> Point"
Но где определен метод
#x:y:
, который описывает обработку одноименного сообщения классом Point
?Чтобы не усложнять систему, вводя какие-то новые сущности или правила, можно воспользоваться уже имеющимся: метод #x:y:
должен быть определен в классе, экземпляром которого является класс Point
. Конструкцию «класс класса» заменим термином «метакласс». Само собой, метакласс Point
можно получить послав сообщение #class
классу Point
:
point class class."-> Point class"
Point class."-> Point class"
Как видим, метаклассу
Point
не присвоено в системе собственное имя. Метаклассы обозначаются через Smalltalk-выражения, с помощью которых их можно получить.Это обстоятельство также показывает, что у каждого класса есть свой собственный метакласс. Больше одного метакласса на класс нам точно не нужно, но и по одному на каждый класс — тоже многовато. Зачем нужно так много?
В Smalltalk-76 на все классы был всего один метакласс. Но если несколько классов имеют один и тот же объект в качестве своего метакласса, то и поведение они имеют одинаковое, так как оно определено именно в метаклассе. Например, сообщение #x:y:
в этом случае можно было бы послать любому классу: String
, Integer
и т.д. Очевидно, это не очень хорошо. Альтернативой является полное отсутствие кастомного поведения на стороне класса (что ничем не лучше), или (еще хуже) ставящая программиста в зависимость от потусторонних сил черная магия на уровне языка — как в большинстве «современных» мейнстримовых творений.
Поэтому, для каждого класса в системе нам нужен один и ровно один метакласс, который определяет поведение этого класса. В Smalltalk метакласс автоматически создается «за кадром» при создании нового класса.
Учитывая, что метаклассы (см. первый принцип) являются объектами и (см. второй принцип) являются экземплярами некоторого класса, делаем вывод, что вышесказанное применимо и к самим метаклассам. Но есть и особенность: в отличие от «обычных» классов, метаклассам не нужно специфичное поведение — они все одинаковым образом хранят поведение своих экземпляров, больше от ничего не требуется. Поэтому уже нет необходимости заводить отдельный (мета-)метакласс на каждый метакласс, достаточно одного объекта, который определит поведение всех метаклассов в системе. Его назвали Metaclass
:
Point class class."-> Metaclass"
Основываясь на тех же принципах, и применяя ту же логику, делаем вывод о том, что
Metaclass
так же должен иметь метакласс:Metaclass class."-> Metaclass class"
Обратите внимание, собственного имени метакласс
Metaclass
-а (за ненадобностью) не удостоился.И еще раз повторяем пройденное: метаклассом Metaclass
(как и всех метаклассов) является Metaclass
:
Metaclass class class."-> Metaclass"
Цепочка замкнулась, вот что у нас получилось:
Мы не затрагивали вопрос наследования, но здесь все просто: поскольку поведение классов наследуется так же, как поведение объектов, суперклассом метакласса C является метакласс суперкласса C:
Point superclass."-> Object"
Point class superclass."-> Object class"
Object superclass."-> ProtoObject"
Object superclass class."-> ProtoObject class"
Данное правило (вынуждено) нарушается только там, где обрывается цепочка наследования: ProtoObject не имеет суперкласса, но его метакласс должен быть классом:
ProtoObject superclass."-> nil"
ProtoObject class superclass."-> Class"
В заключении — пара небольших замечаний, не относящихся к сути дела, но интересных.
Метаклассы не являются полноценными классами, так как первым не требуется вся функциональность последних:
Metaclass superclass."-> ClassDescription"
И для тех, кто захочет полностью закончить картину, но не хочет запускать Smalltalk:
Class superclass."-> ClassDescription"
ClassDescription superclass."-> Behavior"
Behavior superclass."-> Object"
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.
Комментариев нет:
Отправить комментарий