Xpoint
   [напомнить пароль]

Многоэтажная система классов в XPCOM

Метки: [без меток]
2009-12-31 19:16:44 [обр] Илья Коновалов[досье]

Допустим, я описал где-то интерфейс nsIPet:

interface nsIPet : nsISupports {
    attribute string name;
    readonly attribute string type;
    void eat(in string food);
}

Затем я написал на JS модуль, в котором описал класс, реализующий такой интерфейс. Пока все в порядке.
Затем я описал интерфейсы nsICat и nsIDog:

interface nsICat : nsIPet {
    void meow();
}
interface nsIDog : nsIPet {
    void bark();
}

Теперь я хочу реализовать и их тоже, но при этом хочу максимально использовать уже существующий код. Проблема в том, что реализация класса pet находится (теоретически) в другом файле (из-за чего нельзя сказать cat.prototype = new pet()), а копировать/вставлять очень не хочется. Более того, если имеется текстовый файл вида

Tom cat
Jerry mouse
Spike dog

который надо пропарсить и получить список всех петов, не хочется сразу в зависимости от типа пета вызывать нужный конструктор. Вместо этого хочется усложнить QueryInterface класса pet, который бы проверял все "зарегистрированные" типы петов (мало ли, кто-нибудь реализует nsIMouse) и разрешал бы строить объекты cat или dog на основе уже существующего pet.

Вот только как такое правильно сделать?

спустя 1 день 18 часов [обр] Илья Коновалов[досье]

Немного усилю задачу. Положим, в файле для некоторых петов-детей также указаны их воспитатели:

Tyke dog Spike
Tuffy mouse Jerry

В обычном объекте pet и в интерфейсе nsIPet про воспитателей ничего не сказано. Тем не менее, поле tutorName сохраняется где-то внутри объекта. Теперь мы опишем интерфейс nsIPetChild:

interface nsIPetChild : nsIPet {
    attribute nsIPet tutor;
}

При попытке запросить интерфейс nsIPetChild у объекта, реализующего nsIPet, QueryInterface проверит наличие поля tutorName и, если не найдет, вернет ошибку, а если найдет, вернет новый, расширенный объект.

Теперь допустим мы имеет объект tyke. С одной стороны, это дитё и у него есть воспитатель (родитель, если точнее) - Spike. С другой стороны, Tyke - собака. А значит я хочу, чтобы такой код работал:

var tykeChild = tyke.QueryInterface(Ci.nsIPetChild);
var tykeDog = tyke.QueryInterface(Ci.nsIDog);
tykeDog.bark();
dump(tykeChild.tutor.name + " is tutor of " + tyke.name + "\n");

Получается, что либо все объекты - tyke, tykeChild и tykeDog являются экземплярами одного класса, наследующего одновременно классы dog и petChild, либо являются экземплярами разных классов, но внутри себя содержат экземпляр класса pet, на основе которого были построены. Именно последний вариант хотелось бы реализовать.

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

спустя 6 дней [обр] Илья Коновалов[досье]

Получилось, что хотел, не несколько иначе.

Есть некий базовый класс nsIPet. Затем есть дополнительные классы, каждый из которых не является полноценным XPCOM-классом. Им соответствуют "менеджеры", которые в PetManager прописывают расширения. В конструкторе базового класса pet проводится проверка по всем расширениями - если метод test вернул истину, то соответствующий интерфейс добавляется в список интерфейсов, которые реализуются данным объектом.

Например, в классе dog поределены методы test и extend и указано, что pet, расширенный до dog, реализует интерфейс nsIDog. Метод test сводится к проверке this.type == 'dog', а extend к копированию метода bark(). У класса pet_child метод extend устроен чуть сложнее, но суть та же.

Остается вопрос - изобрел ли я велосипед и можно ли это сделать иначе?

Powered by POEM™ Engine Copyright © 2002-2005