[Из песочницы] Аргументированная фабрика
Доброго времени суток! Задача В текущем проекте столкнулся с необходимостью генерировать объекты-наследники от общего интерфейса, при этом порождать наследников необходимо, передавая им в конструкторе некий объект-инициализатор. Конструктора по-умолчанию у классов может и не быть. При этом конкретная фабрика объектов определяется в динамически подгружаемых плагинах, поэтому должна иметь определенный интерфейс. Могу предложить два способа решения данной задачи.Исходные данные Допустим, интерфейс фабрик должен выглядеть следующим образомclass IFactoryBasic { public: IFactoryBasic () {} virtual ~IFactoryBasic () {} virtual Test* create (const QString &key, const QString &args)=0; }; где Test — некий базовый классclass Test { protected: QString _word; public: Test (const QString &word):_word (word) {} virtual ~Test () {} virtual void test (){ qDebug ()<<"test "<<_word; } };
class TestChild: public Test { public: TestChild (const QString &word): Test (word) {} virtual void test () { qDebug ()<<"test child"<<_word; } }; TestChild — наследник Test Оба класса принимают в конструкторе строковый параметр word, который потом мы можем верифицировать в функции test().Первый способ Способ простой. Он основан на создании шаблонного каркаса для будущей фабрики.template class IFactory { QMap handler; protected: void add(const QString &key, C *(T::*func)(const A &)) { handler[key]=func; }
public: IFactory () {} virtual ~IFactory () {} C *make (const QString &key, const A &args) { if (handler.contains (key)) { T* inheritor = dynamic_cast (this); if (inheritor) return (inheritor→*handler[key])(args); } return 0; } }; Здесь есть маленькое обязательство для пользователей класса. Первый шаблонный параметр должен быть классом, который наследуется от IFactory. Далее будут пояснения, для чего это было нужно.handler в классе IFactory — ассоциативный контейнер, содержащий ключ и соответствующую функцию создания объекта. Сигнатура функции порождения описывается как C* (T::*)(const A&), то есть возвращаемое значение будет иметь указатель на некий класс C, как аргумент функции передается ссылка на объект типа A. Функция add (…) добавляет в контейнер пару ключ-функция . Функция make (…) вызывает функцию порождения, если она имеется в контейнере (предварительно динамически преобразовав тип указателя this к типу наследника, иначе нельзя вызвать функции, которые там были определены). Это основной каркас фабрики, осталось описать конкретную фабрикуclass FactoryFst: public IFactory, public IFactoryBasic { Test *createOrigin (const QString &args){ return new Test (args); } Test *createChild (const QString &args) { return new TestChild (args); } public: FactoryFst () { add («test», &FactoryFst: createOrigin); add («testchild», &FactoryFst: createChild); }
Test *create (const QString &key, const QString &args) { return make (key, args); } }; Нетрудно догадаться, что мы используем множественное наследование для удовлетворения требованиям интерфейса IFactoryBasic. Для другого родителя мы явно указываем наследника FactoryFst, возвращаемый указатель будет указателем на объект класса Test, и в качестве аргумента передается ссылка на объект QString. В соответствии с этим определением создаются функции, генерирующие объекты типа Test и TestChild: Test *createOrigin (const QString &args){ return new Test (args); } — создает объект типа Test, передавая ему в конструктор аргумент QString.Test *createChild (const QString &args) { return new TestChild (args); } — аналогично создает объект типа TestChild. Остается только в конструкторе FactoryFst зарегистрировать данные функции и определить функцию create (…) интерфейса IFactoryBasic.Второй способ Этот метод в большей степени использует шаблоны. Для построения фабрики нам нужна небольшая подготовка. Для начала нужно определить некоторые используемые фабрикой классы. Для начала определим вспомогательный класс для хранения значений передаваемого аргумента. Для того, чтоб можно было беспрепятственно использовать указатели на данный класс, сделаем его наследуемым от нешаблонного базового класса.Читать дальше →