Можно ли создать фабричную систему C++, которая может создавать экземпляр любого "зарегистрированного" типа объекта, независимо от наследования?
Я провел весь день, исследуя эту тему, так что с некоторыми разбросанными знаниями по теме я пришел к вам с этим расследованием. Пожалуйста, позвольте мне описать то, что я пытаюсь выполнить, и, возможно, вы можете предложить решение ближайшего вопроса или другой способ решения проблемы полностью.
Я пытаюсь имитировать что-то, связанное с тем, как файлы XAML работают в WPF, где вы, по сути, создаете экземпляр дерева объектов из определения XML. Если это неправильно, пожалуйста, сообщите. В остальном эта проблема не связана с WPF, C# или чем-либо управляемым - я упоминаю об этом только потому, что это похожая концепция.
Итак, я уже создал класс XML- анализатора и сгенерировал дерево узлов на основе объектов ObjectNode. Объекты ObjectNode содержат строковое значение с именем type, и у них есть std::vector дочерних объектов ObjectNode.
Следующим шагом является создание экземпляра дерева объектов на основе данных в дереве ObjectNode. Это промежуточное дерево ObjectNode необходимо, потому что одно и то же дерево ObjectNode может создаваться несколько раз или задерживаться по мере необходимости. Дерево объектов, которое создается, таково, что узлы в дереве являются потомками общего базового класса, который сейчас мы можем называть MyBase. Конечные узлы могут быть любого типа, не обязательно производными от MyBase.
Чтобы сделать это более сложным, я не буду знать, какие типы объектов могут быть задействованы в дереве, поэтому мне нужно разрешить регистрировать новые типы на фабрике.
Я в курсе фабрики буста. У их документов есть интересный маленький абзац дизайна на этой странице:
o Мы можем захотеть фабрику, которая принимает некоторые аргументы, которые передаются в конструктор,
o мы, вероятно, захотим использовать умные указатели,
o мы можем захотеть, чтобы несколько функций-членов создавали различные виды объектов,
o нам не обязательно нужен полиморфный базовый класс для объектов,
o как мы увидим, нам вообще не нужен базовый класс фабрики,
o мы могли бы просто вызвать конструктор - без #new#, чтобы создать объект в стеке, и
o наконец, мы могли бы использовать настраиваемое управление памятью.
Возможно, я не все правильно понимаю, но, похоже, это говорит о том, что то, что я пытаюсь сделать, может быть достигнуто с помощью фабрики Boost. Но все примеры, которые я нашел, похоже, описывают фабрики, где все объекты получены из базового типа.
Любое руководство по этому вопросу будет принята с благодарностью.
Спасибо за ваше время!
4 ответа
Определите typedef для фабричных методов, например:
typedef void* (*FactoryMethod)(const vector<string>& parameters);
Другими словами, каждый метод фабрики возвращает указатель на что-либо (указатель на void) и принимает в качестве параметра постоянный вектор строк.
Регистрационные данные выглядят так:
std::map<string, FactoryMethod> globalFactoryMethods;
... где ключ это тип string
и значение является указателем на соответствующий фабричный метод. Если у вас есть новый тип с новым фабричным методом, "зарегистрируйте" его, добавив на карту (во время выполнения).
Конструкция с использованием зарегистрированных фабричных методов может выглядеть следующим образом (псевдокод):
foreach (Node node in nodes)
{
string type = node.type;
FactoryMethod factoryMethod = globalFactoryMethods[type];
void* constructedLeaf = (*factoryMethod)(node.nParameters, node.pParameters);
//do something here with constructed leaf
}
Конечные узлы могут быть любого типа, не обязательно производными от MyBase.
Это проблема в C++: если вы не знаете, что это такое, то как вы их удалите?
C# (в отличие от C++) может удалять вещи, не зная, что они, или, скорее, не нужно удалять вещи: потому что это управляемая память, и все является подклассом System.Object
,
Очень хорошо, что вы описали свою проблему:)
Идея иметь фабрику, способную создавать любой тип шрифта, конечно, очень соблазнительна, однако она сложна и ненужна. Я понимаю привлекательность void*
к сожалению, это не так гламурно, как кажется. Единственное, что вы можете сделать с таким зверем, это привести его к правильному указателю... и это требует знания типа во время компиляции!
Предположим, у меня есть std::vector<void*>
и я прошу вас распечатать содержание этого вектора, что вы собираетесь делать?
Вполне допустимо заставлять людей иметь создаваемые ими классы, наследуемые от базового класса, который вы разрабатываете с целью облегчения работы на фабрике.
struct Object: boost::noncopyable
{
virtual Object* clone() const = 0;
virtual ~Object() {}
};
Это действительно очень небольшое ограничение, чем требование наследования от этого класса. В обмен вы получаете виртуальный деструктор и гарантируете, что класс может быть скопирован полиморфно (благодаря clone
функция-член), чтобы избежать проблемы нарезки объектов.
Я думаю, что вы должны прочитать некоторые шаблоны проектирования:
- Прототип широко используется для реализации Factory, хотя это не единственный способ
Factory
кажется, вы уже знаете, возможно, чтоAbstractFactory
тебе тоже поможет- Композит является обязательным для работы с деревьями объектов
Visitor
действительно помогает определить прямой совместимый способ работы наComposite
Я не могу понять ваш вопрос, но из темы, я думаю, вы пытаетесь создать произвольный зарегистрированный объект, возможно, вы можете посмотреть на шаблон проектирования "прототип"
Нажмите здесь для всемогущего вики
Если я думаю, что я правильно понял что-то вроде этого, возможно:
void register(string constructor, void*(*method)(...)); // put in map
template<class T>
T* new_(string subclass, ...) {
return static_cast<T*>(constructors_[subclass](...));
}