Можно ли создать фабричную систему C ++, которая может создавать экземпляр любого «зарегистрированного» типа объекта, независимо от наследования? - PullRequest
4 голосов
/ 24 мая 2010

Я провел весь день, исследуя эту тему, так что с некоторыми разбросанными знаниями по теме я пришел к вам с этим запросом.Пожалуйста, позвольте мне описать то, что я пытаюсь выполнить, и, возможно, вы можете предложить решение ближайшего вопроса или другой способ решения проблемы полностью.

Я пытаюсь имитировать что-то, связанное с тем, как *Файлы 1003 * XAML работают в WPF , где вы по существу создаете экземпляр дерева объектов из определения XML.Если это неправильно, пожалуйста, сообщите.В остальном эта проблема не связана с WPF , C # или чем-либо управляемым - я упоминаю об этом только потому, что это похожая концепция ..

Итак, я создалкласс синтаксического анализатора XML и сгенерированное дерево узлов на основе объектов ObjectNode . ObjectNode объекты содержат строковое значение с именем type , и у них есть std :: vector дочерних ObjectNode объектов.

Следующим шагом является создание экземпляра дерева объектов на основе данных в дереве ObjectNode .Это промежуточное дерево ObjectNode необходимо, потому что одно и то же дерево ObjectNode может быть создано несколько раз или отложено по мере необходимости.Дерево объектов, которое создается, таково, что узлы в дереве являются потомками общего базового класса, который на данный момент мы можем назвать MyBase .Листовые узлы могут быть любого типа, необязательно производными от MyBase .

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

Я знаю о заводе Boost.У их документов есть интересный маленький абзац дизайна на этой странице:

o Мы можем захотеть фабрику, которая принимает некоторые аргументы, которые передаются конструктору,
o мы будемвероятно, нам нужно использовать умные указатели,
o нам может потребоваться несколько функций-членов для создания объектов различного типа,
o нам может не потребоваться полиморфный базовый класс для объектов,
o, как мы увидим,нам вообще не нужен базовый класс фабрики,
o нам может потребоваться просто вызвать конструктор - без # new # для создания объекта в стеке и
o наконец, мы можем захотеть использовать настраиваемое управление памятью.

Возможно, я не все правильно понимаю, но, похоже, это говорит о том, что то, что я пытаюсь сделать, может быть достигнуто с помощью Boost.Но все примеры, которые я нашел, похоже, описывают фабрики, где все объекты получены из базового типа.

Будем весьма благодарны за любые рекомендации по этому вопросу.

Спасибо за ваше время!

Ответы [ 4 ]

2 голосов
/ 24 мая 2010

Определите 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.

1 голос
/ 24 мая 2010

Очень хорошо, что вы описали свою проблему:)

Идея иметь фабрику, способную создавать любой тип шрифта, конечно, очень заманчива, однако она сложна и ненужна. Я понимаю привлекательность void*, к сожалению, это не так гламурно, как кажется. Единственное, что вы можете сделать с таким зверем, это привести его к правильному указателю ... и это требует знания типа во время компиляции!

Предположим, у меня есть std::vector<void*>, и я прошу вас распечатать содержимое этого вектора, что вы собираетесь делать?

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

struct Object: boost::noncopyable
{
  virtual Object* clone() const = 0;
  virtual ~Object() {}
};

Это действительно очень маленькое ограничение, чем требование наследования от этого класса. Взамен вы получаете виртуальный деструктор и гарантируете, что класс можно копировать полиморфно (благодаря функции-члену clone), чтобы избежать проблемы среза объекта.

Я думаю, вам следует прочитать некоторые шаблоны проектирования:

  • Прототип широко используется для реализации Factory, хотя это не единственный способ
  • Factory, вы, кажется, уже знаете, возможно, что AbstractFactory тоже поможет вам
  • Композит является обязательным для работы с деревьями объектов
  • Visitor действительно помогает определить прямой совместимый способ работы на Composite
0 голосов
/ 24 мая 2010

Я не могу понять ваш вопрос, но из темы, я думаю, вы пытаетесь создать произвольный зарегистрированный объект, возможно, вы можете посмотреть на шаблон проектирования "прототип"
Нажмите здесь для всемогущей вики

0 голосов
/ 24 мая 2010

если я думаю, что правильно понял, что-то вроде этого возможно:

void register(string constructor, void*(*method)(...)); // put in map

template<class T>
T* new_(string subclass, ...) {
  return static_cast<T*>(constructors_[subclass](...));
}
...