Многоэтажная система классов в XPCOM
Допустим, я описал где-то интерфейс 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.
Вот только как такое правильно сделать?
Немного усилю задачу. Положим, в файле для некоторых петов-детей также указаны их воспитатели:
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, на основе которого были построены. Именно последний вариант хотелось бы реализовать.
В целях эксперимента начал писать тестовый набор компонентов для этого случая, если получится, поделюсь. Но хочется знать, не изобретаю ли я велосипед.
Получилось, что хотел, не несколько иначе.
Есть некий базовый класс nsIPet. Затем есть дополнительные классы, каждый из которых не является полноценным XPCOM-классом. Им соответствуют "менеджеры", которые в PetManager прописывают расширения. В конструкторе базового класса pet проводится проверка по всем расширениями - если метод test вернул истину, то соответствующий интерфейс добавляется в список интерфейсов, которые реализуются данным объектом.
Например, в классе dog поределены методы test и extend и указано, что pet, расширенный до dog, реализует интерфейс nsIDog. Метод test сводится к проверке this.type == 'dog', а extend к копированию метода bark(). У класса pet_child метод extend устроен чуть сложнее, но суть та же.
Остается вопрос - изобрел ли я велосипед и можно ли это сделать иначе?
![[logo]](/site/images/logo.jpg)