14.1 Идея |
Предыдущая Содержание Следующая |
Каждый динамически скомпонованный метод вызывается через свой селектор и мы видели в главе 8, что селектор проверяет, может ли метод быть найден для объекта. В качестве примера предположим наличие селектора add() для метода добавления объекта к List:
struct Object * add (void * _self, const void * element) { struct Object * result; const struct ListClass * class = cast(ListClass(), classOf(_self));
assert(class -> add.method); cast(Object(), element);
result = ((struct Object * (*) ()) class -> add.method)(_self, element); return result; }
classOf() пытается удостовериться, что _self ссылается объект; окружение вызова cast() устанавливает, что описание класса _self принадлежит метаклассу ListClass, то есть что оно действительно содержит указатель на метод add; наконец, assert() защищает от нулевого значения, выдающего себя за этот указатель, то есть удостоверяется, что где-нибудь в цепочке наследования метод add был реализован. Что происходит, если add() применяется к объекту, который никогда не слышал об этом методе, то есть что происходит, если _self проваливает различные тесты в селекторе add()? В настоящий момент где-нибудь в месте проблемы срабатывает assert() и наша программа завершается. Предположим, что мы работаем с классом объектов X, которые сами по себе не являются потомками List, но которые знают некоторый объект List, которому они могли бы по логике передать запрос к add(). В настоящий момент за то, чтобы узнать (или найти с помощью respondsTo()), что add() не может быть применён к нему и соответственно перенаправить вызов, ответственность нёс бы пользователь объектов X. Однако, рассмотрим следующий немного переделанный селектор:
struct Object * add (void * _self, const void * element) { struct Object * result; const struct ListClass * class = (const void *) classOf(_self);
if (isOf(class, ListClass()) && class -> add.method) { cast(Object(), element); result = ((struct Object * (*) ()) class -> add.method)(_self, element); } else forward(_self, & result, (Method) add, "add", _self, element); return result; }
Теперь _self может ссылаться на любой объект. Если оказывается, что его класс действительно содержит указатель add, этот метод вызывается как прежде. Иначе вся информация передаётся новому методу forward(): сам объект; категория ожидаемого результата; имя и указатель на селектор, потерпевший неудачу; и значения в исходном списке аргументов. forward() — самостоятельный динамически компонуемый метод, задекларированный в Object:
% Class Object { ... %- void forward (const _self, void * result, \ Method selector, const char * name, ...);
Очевидно, первоначальное определение немного бесполезно:
% Object forward { %casts fprintf(stderr, "%s at %p does not answer %s\n", nameOf(classOf(self)), self, name); assert(0); }
Если сам Object не понимает метод, мы неудачники. Однако forward() компонуется динамически: если класс хочет передать сообщения, он может сделать это, переопределив forward(). Как мы увидим в примере раздела 14.6, это почти столь же хорошо, как объект, принадлежащий нескольким классам одновременно.
|
Предыдущая Содержание Следующая |