Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Шаблон фабрики объектов

505
16 января 2009 года
vAC
343 / / 28.02.2006
На днях откопал свой сделанный на скорую руку исходник с шаблоном фабрики объектов с
возможностью преобразования продуктов в последовательную форму. Хотелось бы доработать его, довести до максимально гибкого состояния, чтобы можно было легко использовать в других проектах. Поэтому и решил выложить его на обозрение общественности. Исходники находятся в репозитории Subversion по адресу:
http://subversion.assembla.com/svn/djilyqEmSr3iSfabIlDkbG
Клиент для windows можно скачать отсюда:
http://tortoisesvn.net/downloads
Проект под VS2005, unmanaged C++. Никаких дополнительных библиотек для тестовой программы не требуется.

Кратко опишу сущности, которые я ввел для шаблона:
1. Создатель - сущность, производящая и сериализирующая продукт.
2. Продукт - собственно то, что производится создателем.
3. Фабрика - реестр создателей. Каждому создателю при регистрации присваивается идентификатор, по которому и можно создавать продукты.
4. Упаковка - пара "продукт/идентификатор создателя". Это и есть результат создания продукта через фабрику.
Для сериализации, помимо самого продукта, требуется информация о его создателе, поэтому и введена данная
сущность.

Исходник был в проекте под Qt. В качестве хранилища использовался класс QSettings, в качестве идентификаторов создателей QUuid. Но отрабатывать шаблон лучше на STL, поэтому и набросал простенький
примерчик с уточняющей реализацией под потоки ввода/вывода.
В качестве продукта выбрана строка (std::string), идентификатора - int.
Синонимы типов идентификатора, создателя и фабрики:
 
Код:
typedef int id_t;
 typedef tlib::factory::Stl_store_creator<string, id_t> creator_t;
 typedef tlib::factory::Stl_store_factory<creator_t> factory_t;

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

Жду вашей критики и предложений...
562
16 января 2009 года
tarekon
175 / / 19.08.2003
В принципе, ничо так :), но:

1) Я бы всё-таки не стал смешивать сериализацию и фабрики объектов. Имхо, сериализовывать сложный объект, состоящий из кучи объектов, сконструированных таким образом, тяжело. Да и по смыслу как-то не очень. Если очень хочется такой сериализатор - надо создавать его как обычный объект, через фабрику.

2) Использовать везде передачу параметра по значению - это сурово. Особенно сурово так делать с std::map в Factory::get_creator_map(). Лучше максимально возможно использовать константные ссылки.

3) Не понял, зачем такие сложности с фабрикой - почему она шаблонная? Казалось бы, все CREATOR'ы наследуются от ICreator как раз для наличия "наибольшего общего делителя".

4) Использование CREATOR_PTR как задаваемого извне - это фатально. В данном примере Stl_store_factory определяет его как обычный сишный указатель. Если кто-то уничтожит объект Creator снаружи, то фабрика сможет сделать AV. Либо, что еще хуже, отработать с этим, уже удаленным объектом. Самое страшное, что удаление с AV могут произойти в сильно разнесенных по коду и времени местах. Отлаживать такое тяжело. Используй умные указатели, благо, у тебя есть доступ к базовому классу.

5) На мой взгляд, строк вполне хватает для идентификаторов, и делать их тип параметром шаблона излишне.
505
17 января 2009 года
vAC
343 / / 28.02.2006
Цитата: tarekon

1) Я бы всё-таки не стал смешивать сериализацию и фабрики объектов. Имхо, сериализовывать сложный объект, состоящий из кучи объектов, сконструированных таким образом, тяжело. Да и по смыслу как-то не очень. Если очень хочется такой сериализатор - надо создавать его как обычный объект, через фабрику.


Т.е. вы предлагаете разделить одну фабрику на две: создающую объекты продуктов и объекты, сериализирующие продукты первой фабрики? Надо посмотреть, что получится, решение более гибкое.

Цитата: tarekon

2) Использовать везде передачу параметра по значению - это сурово. Особенно сурово так делать с std::map в Factory::get_creator_map(). Лучше максимально возможно использовать константные ссылки.


Здесь согласен. Factory::get_creator_map - первый кандидат на вылет, для запроса таблицы создателей можно будет применить более гибкий паттерн visitor. Но и пользоваться ссылками нужно тоже продумав все варианты. Например, в качестве продукта может быть ссылка на объект, а ссылка на ссылку в С++ не существует.

Цитата: tarekon

3) Не понял, зачем такие сложности с фабрикой - почему она шаблонная? Казалось бы, все CREATOR'ы наследуются от ICreator как раз для наличия "наибольшего общего делителя".


Шаблонная, потому что гибкая. Гибкая, потому что шаблонная. Не будь шаблона - приходилось бы использовать динамическое приведение типов вниз со всеми вытекающими последствиями.

Цитата: tarekon

4) Использование CREATOR_PTR как задаваемого извне - это фатально. В данном примере Stl_store_factory определяет его как обычный сишный указатель. Если кто-то уничтожит объект Creator снаружи, то фабрика сможет сделать AV. Либо, что еще хуже, отработать с этим, уже удаленным объектом. Самое страшное, что удаление с AV могут произойти в сильно разнесенных по коду и времени местах. Отлаживать такое тяжело. Используй умные указатели, благо, у тебя есть доступ к базовому классу.


В реализации для STL я намеренно сделал CREATOR_PTR как обычный указатель, т.к. в STL (в стандарте от 2003, в 2009м этот пробел заполнят ;-) ) нет умного указателя, подходящего для корректного хранения в контейнерах.
Это такое же фатальное как просто использовать глупые указатели в программе, от программиста в любом случае потребуется внимание при использовании таких указателей. Если вы не согласны с параметром по-умлочанию, вы всегда можете его изменить. Не внося изменений во внутренности фабрики я без всяких проблем могу перейти на умные указатели:

 
Код:
typedef tlib::factory::Stl_store_factory<creator_t, boost::shared_ptr<creator_t>> factory_t;

В я не стал использовать какую-то конкретную библиотеку внутри шаблона, т.к. это опять таки снижает гибкость шаблона. Может кто-то предпочетает, например, библиотеку loki.
Этот пример тоже идет в пользу аргумента: "Шаблонная, потому что гибкая".

Цитата: tarekon

5) На мой взгляд, строк вполне хватает для идентификаторов, и делать их тип параметром шаблона излишне.


И опять таки это сделано по причине того же аргумента.

562
17 января 2009 года
tarekon
175 / / 19.08.2003
Цитата: vAC
Т.е. вы предлагаете разделить одну фабрику на две: создающую объекты продуктов и объекты, сериализирующие продукты первой фабрики? Надо посмотреть, что получится, решение более гибкое.



У нас принято сериализацию реализовывать в самом объекте, передавая ему на вход указатель на интерфейс хранилища. Это не идеально гибкий способ, но, как показывает практика, в 99 процентах случаев хватает за глаза.
Кстати, я программист, ко мне можно на "ты" :)

Цитата: vAC
Здесь согласен. Factory::get_creator_map - первый кандидат на вылет, для запроса таблицы создателей можно будет применить более гибкий паттерн visitor. Но и пользоваться ссылками нужно тоже продумав все варианты. Например, в качестве продукта может быть ссылка на объект, а ссылка на ссылку в С++ не существует.


Имеет ли смысл продукт в виде сишной ссылки?

Цитата: vAC
Шаблонная, потому что гибкая. Гибкая, потому что шаблонная. Не будь шаблона - приходилось бы использовать динамическое приведение типов вниз со всеми вытекающими последствиями.


Разве приведение потребуется? По-моему, только виртуальные вызовы.
Тут скорее моё субъективное предпочтение - если можно не использовать шаблоны-не используй.

Цитата: vAC
В реализации для STL я намеренно сделал CREATOR_PTR как обычный указатель, т.к. в STL (в стандарте от 2003, в 2009м этот пробел заполнят ;-) ) нет умного указателя, подходящего для корректного хранения в контейнерах.
Это такое же фатальное как просто использовать глупые указатели в программе, от программиста в любом случае потребуется внимание при использовании таких указателей. Если вы не согласны с параметром по-умлочанию, вы всегда можете его изменить. Не внося изменений во внутренности фабрики я без всяких проблем могу перейти на умные указатели:
 
Код:
typedef tlib::factory::Stl_store_factory<creator_t, boost::shared_ptr<creator_t>> factory_t;

В я не стал использовать какую-то конкретную библиотеку внутри шаблона, т.к. это опять таки снижает гибкость шаблона. Может кто-то предпочетает, например, библиотеку loki.
Этот пример тоже идет в пользу аргумента: "Шаблонная, потому что гибкая".



Этот тип не используется во внешнем интерфейсе (метод find не в счет, т.к. там можно вернуть и сишный указатель), а используется только во внутренних механизмах класса. На мой взгляд, здесь не тот случай, когда полезно отдавать наружу внутреннюю логику класса - никаких бонусов с этого нет, одни только грабли.

505
19 января 2009 года
vAC
343 / / 28.02.2006
Цитата: tarekon
У нас принято сериализацию реализовывать в самом объекте, передавая ему на вход указатель на интерфейс хранилища. Это не идеально гибкий способ, но, как показывает практика, в 99 процентах случаев хватает за глаза.
Кстати, я программист, ко мне можно на "ты"


На самом деле я не про сам процесс сериализации говорил, а об управляющей составляющей этого процесса.

Самым интересным было бы упростить процесс сериализации полиморфных объектов. Такая задача стоит, например, при использовании подключаемых модулей (Plug-ins),
где реализация скрыта внутри модуля.


Цитата: tarekon
Имеет ли смысл продукт в виде сишной ссылки?


а почему бы и нет?

Цитата: tarekon
Разве приведение потребуется? По-моему, только виртуальные вызовы.

Тут скорее моё субъективное предпочтение - если можно не использовать шаблоны-не используй.


тогда что будет результатом ICreator::make в случае его нешаблонности?
А мое субъективное предпочтение - использовать шаблоны там, где это поможет обобщению и повторному использованию кода. Фабрики объектов как раз таковыми и являются.

Цитата: tarekon
Этот тип не используется во внешнем интерфейсе (метод find не в счет, т.к. там можно вернуть и сишный указатель), а используется только во внутренних механизмах класса. На мой взгляд, здесь не тот случай, когда полезно отдавать наружу внутреннюю логику класса - никаких бонусов с этого нет, одни только грабли.



Еще есть регистрация создателя, и от выбранного типа указателя будет зависеть клиентский код. Зачем мне навязывать ту или иную библиотеку, когда я могу предоставить выбор? Я абсолютно не вижу в этом никаких граблей. Повторюсь, что использование шаблонного параметра по-умолчанию не навязывается, используй любой тип указателей. Если очень необходимо, то можно унаследоваться от этого шаблона с конкретным умным указателем и использовать его. Логика работы фабрики с указателями тривиальна - нужно обеспесить их валидность на протяжении всей жизни фабрики и все. В моем простейшем примере это обеспечивается стеком.
Сама по себе свобода выбора - это уже бонус. Или, например, нам надо посчитать сколько было обращений к каждому из создателей. Для этого достаточно изменить тип указателя на тот, в котором происходит подсчет обращений к селектору.

505
19 января 2009 года
vAC
343 / / 28.02.2006
Думаю будет разумным периодически выкладывать архив.
505
19 января 2009 года
vAC
343 / / 28.02.2006
Добавил пример фабрики полиморфных продуктов.
Идея состоит в том, что продуктом является указатель на некоторый интерфейсный класс IProduct.
Производными от него являются String и Numeric. Причем создатель может создавать не только один и тот же тип продукта,
но может и менять его в процессе выполнения программы (Mixed_creator). Поэтому пришлось частично возложить запись пакета
на сам создатель, т.к. только он имеет информацию о возможных типах продуктов. Для сериализации таких продуктов сделан специальный
сериализатор Poly_serializator, который оставляет для реализации только сериализацию идентификатора, сериализация самого продукта осуществляется
вызовом соответствующего метода создателя. Прикладываю схему примера и архив.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог