14.1 Идея

Предыдущая  Содержание  Следующая V*D*V

Каждый динамически скомпонованный метод вызывается через свой селектор и мы видели в главе 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, это почти столь же хорошо, как объект, принадлежащий нескольким классам одновременно.

 

Предыдущая  Содержание  Следующая