Distributed under the Boost Software License, Version 1.0. (См. сопроводительный файл LICENSE_1_0.txt или копия на http://www.boost.org/LICENSE_1_0.txt)
Шаблон boost::factory позволяет инкапсулировать new выражение как объект функции, boost:: value_factory инкапсулирует конструктора без new.
boost::factory<T*>()(arg1,arg2,arg3)// same as new T(arg1,arg2,arg3)boost::value_factory<T>()(arg1,arg2,arg3)// same as T(arg1,arg2,arg3)
По техническим причинам аргументы для функциональных объектов должны быть LValues. Фабрика, которая также принимает RValues, может быть составлена с использованием boost::forward_adapter или boost::bind>.
В традиционном ориентированном на объект программировании фабрика является объектом, реализующим интерфейс одного или нескольких методов, которые строят объекты, соответствующие известным интерфейсам.
// assuming a_concrete_class and another_concrete_class are derived// from an_abstract_classclassa_factory{public:virtualan_abstract_class*create()const=0;virtual~a_factory(){}};classa_concrete_factory:publica_factory{public:virtualan_abstract_class*create()const{returnnewa_concrete_class();}};classanother_concrete_factory:publica_factory{public:virtualan_abstract_class*create()const{returnnewanother_concrete_class();}};// [...]intmain(){boost::ptr_map<std::string,a_factory>factories;// [...]factories.insert("a_name",std::auto_ptr<a_factory>(newa_concrete_factory));factories.insert("another_name",std::auto_ptr<a_factory>(newanother_concrete_factory));// [...]std::auto_ptr<an_abstract_class>x(factories.at(some_name).create());// [...]}
Этот подход имеет несколько недостатков. Самым очевидным является то, что существует множество котел-кодов. Другими словами, существует слишком много кода, чтобы выразить довольно простое намерение. Мы могли бы использовать шаблоны, чтобы избавиться от некоторых из них, но подход остается негибким:
Нам может понадобиться фабрика, которая принимает некоторые аргументы, которые направляются конструктору,
мы, вероятно, хотим использовать умные указатели,
мы можем хотеть несколько функций-членов для создания различных видов объектов,
нам не обязательно нужен полиморфный базовый класс для объектов,
как мы увидим, нам вообще не нужен заводский базовый класс,
мы можем просто позвонить конструктору - без new создать объект на стеке, и
наконец, мы можем использовать индивидуальные управления памятью.
Опыт показал, что использование функциональных объектов и общих компонентов Boost для их состава, Design Patterns, которые описывают механизмы обратного вызова (как правило, требующий высокого процента котел-кода с чистой методологией объект-ориентированной) становятся реализуемыми с помощью всего нескольких строк кода и без дополнительных классов.
Фабрики - это механизмы обратного вызова для конструкторов, поэтому мы предоставляем два шаблона класса: boost:: value_factory и boost::factory, которые инкапсулируют конструкцию объекта путем прямого применения конструктора и new оператора, соответственно.
Мы позволяем функциям направлять свои аргументы в пользу выражений, которые они инкапсулируют. Над этим boost::фабрикатор опционально позволяет использовать умные указатели и Аллократы.
Полиморфизм комбинированного времени может использоваться там, где это необходимо,
template<classT>voiddo_something(){// [...]Tx=T(a,b);// for conceptually similar objects x we neither need virtual// functions nor a common base class in this context.// [...]}
Теперь, чтобы разрешить неоднородные подписи для конструкторов типов, переданных для T, мы можем использовать value_factory и boost::bind для нормализации между ними.
template<classValueFactory>voiddo_something(ValueFactorymake_obj=ValueFactory()){// [...]typenameValueFactory::result_typex=make_obj(a,b);// for conceptually similar objects x we neither need virtual// functions nor a common base class in this context.// [...]}intmain(){// [...]do_something(boost::value_factory<X>());do_something(boost::bind(boost::value_factory<Y>(),_1,5,_2));// construct X(a,b) and Y(a,5,b), respectively.// [...]}
Может быть, мы хотим, чтобы наши объекты пережили объем функции, в этом случае мы должны использовать динамические распределения;
template<classFactory>whateverdo_something(Factorynew_obj=Factory()){typenameFactory::result_typeptr=new_obj(a,b);// again, no common base class or virtual functions needed,// we could enforce a polymorphic base by writing e.g.// boost::shared_ptr<base>// instead of// typename Factory::result_type// above.// Note that we are also free to have the type erasure happen // somewhere else (e.g. in the constructor of this function's// result type).// [...]}// [... call do_something like above but with __factory__ instead// of __value_factory__]
Хотя мы могли создать полиморфные объекты в предыдущем примере, мы использовали полиморфизм времени компиляции для фабрики. Если мы хотим стереть тип фабрики и таким образом разрешить полиморфизм во время запуска, мы можем использовать Boost.Function для этого. Первый пример можно переписать следующим образом.
Шаблон функционального объекта, который динамически конструирует объект указателя для типа указателя, заданного как аргумент шаблона. Умные указатели могут быть использованы для аргумента шаблона, учитывая, что boost::pointee<Pointer>::type дает указательный тип.
Если дается Аллоктор, он используется для распределения памяти и для построения объекта используется форма размещения new. Функциональный объект, который называет деструктором и передает память с копией Аллокатора, используется для второго аргумента конструктора Pointer (так это должен быть Smart Pointer, который предоставляет подходящий конструктор, такой как boost::shared_ptr).
Если третий аргумент шаблона factory_passes_alloc_to_smart_pointer, то сам аллокат используется для третьего аргумента конструктора Pointer (boost::shared_ptr затем использует аллокататор для управления памятью отдельно выделенного контрольного счетчика).
Для того, чтобы снять зависимость от Boost. Опционный параметр по умолчанию для аллоцаторов был изменен с boost::none_t на void. Если у вас есть код, который перестал работать, потому что он использует boost::none_t, быстрое исправление заключается в определении BOOST_FUNCTIONAL_FACTORY_SUPPORT_NONE_T, которое восстановит поддержку, но это будет удалено в будущем выпуске. Это должно быть относительно легко исправить должным образом.
Эрик Ниблер попросил функцию вызвать конструктора типа (с аргументами, представленными как Тупле) в качестве функции Fusion. Эти заводские утилиты - факторинговое обобщение этой идеи.
Дэйв Абрахамс предложил поддержку Smart Pointer для безопасности исключений, предоставляя полезные подсказки для реализации.
Стиль документации Джоэля де Гузмана был скопирован из Fusion.
Кроме того, я хочу поблагодарить Питера Димова за то, что он поделился своими знаниями о языковых деталях и их эволюции.
Последний пересмотр: 1 ноября 2008 года в 21:44:52 GMT
Статья Chapter 1. Boost.Functional/Factory 1.0 раздела Chapter 1. Boost.Functional/Factory 1.0 может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.