R>>Нет, там другое. R>>Часть функциональности, о которой я говорю там есть. Но только часть, причём не главная. Там нет саморегистрации. А>ИМХО, как раз основое (оно же главное) там есть. Добавлена только саморегистрация, что есть довольно сомнительное приобретение.
Ну нет же, совсем нет так. В Loki есть только фабрика. Да она хорошо и удобно сделана. Но это давно известная и так сказать простая вещь. Всё что она делает создаёт по идентификатору типа объект типа. И не более.
Я же говорю, о более высокоуровневой функциональности, о метаклассах. Т.е. появляется возможность делать такие вещи как перебрать все типы, получить какие-то их свойства, получить название типа, которое можно вывести пользователю. И всё это удобно для пользователей инфраструктуры. Ничего даже рядом нет в Loki, там есть только создание объектов.
Создание объектов, конечно, важная функциональноть. Но не главная. Если бы я решил только эту задачу, то я бы не стал заводить топик.
А>В "классической" реализации фабрики классов есть возможность собрать вызовы Register в одном месте, что очень удобно при большом количестве "продуктов".
Вопрос спорный. То, о чём говоришь ты, как раз называют плохо расширяемым дизайном. Когда в одном месте много чего-то и это место надо постоянно править. А когда данные распределены и локализованы, это называют расширяемым дизайном. Добавил класс в одном месте и всё заработало. Не надо править в нескольких местах, не надо трогать старый отлаженный код, как предлагаешь ты.
А>В твоём же случае, когда тебе понадобиться добавить очередной продукт, придётся обшарить все модули с целью узнать следующее незанятое значение TYPE. А их может быть несколько больше, чем один.
Это зависит от идентификатора класса. Если это например название элемента в XML-файле, то не придётся лазить, что бы найти следующий неиспользованный идентификатор. Потомучто это уже проектировщик решит, что элемент называется, например, "person".
Или, если например, если идентификаторы связаны с каким-то протоколом. То тоже не придётся лазить, уже известно, что поддерживаем новый тип с кодом, например, 47.
R>>Там нет возможности нагружать эту инфроструктуру дополнительной функциоанльностью. То о чём я говорю — своего рода метаинформация или рефлекшн — и соответственно код на более высоком уровне. А>Вот тут хотелось бы поподробнее. Смысл фабрики — сокрытие типов. Откуда возьмётся информация?
А>
IS_SIMPLE относится не к экземпляру, а к типу. Не путай это. Т.е это должно выглядеть как:
bool IsSimple = HeaderFactory::IsSimpleType(48);
Хотя, конечно, метод IsSimpleType() можно вынести в IHeader, а его реализацию в Header<>, тогда будет работать как ты хочешь.
R>>Хотя, возможно, мой класс фабрики можно относледовать от класса Loki::Factory, что бы заюзать часть функциональности. А>)))))
Не понимаю, чего смешного
R>>Плюсы: R>>1. Практически нулевое дублирование, что важно для сопровождаемого кода А>) R>>2. Инрастуктура функциональная, расширяемая и "умная" А>Loki::Factory R>>3. Сами классы реализаций интерфейсов абсолютно самодостаточные, самоописываемые, т.е. высокая локальность данных и R>функций А>Это уж как их реализует пользователь этой фабрики. R>>4. Чтобы добавить новый класс реализации надо просто его описать и его поддержка автоматически появиться везде где R>она должна быть. Чем не может похвастаться традиционный подход — саму реализацию добавил, добавил её поддержку в R>фабрику, добавил её поддержку в интерфейс пользователя, а при загрузке из БД забыл. А>Про обратную сторону медали этого удобства я уже писал.
А>Резюмируя: к Loki::Factory добавлено две примочки, одна из которых может быть полезна при условии, что реализация продуктов сконцентрирована в очень небольшом числе модулей, а вторая просто бессмыслена. А>)
Мне лично, она не кажется бессмысленной. Я применял такое решение на практике. И получал от него вполне практическую выгоду.
" Аноним " <0@users.rsdn.ru> сообщил/сообщила в новостях следующее: news:1789146@news.rsdn.ru... > Так это, с чего всё начиналось - > да, switch'а нет, зато есть std::map, что в плане производительности ещё хуже
ИМХО в данной, весьма общей, постановке задачи больший интерес представляет построение максимально универсального решения, а не быстрого и специализированного. Тем более, что во многих случаях будет возможность оптимизацию по скорости сделать дополнительной примочкой к общему решению. Например можно реализовать автоматическое присвоение числовых идентификаторов классов в ран тайме, если строить дополнительный статический мап для перекодировки строки в число. А доступ к идентификатору предоставлять через статичекскую переменную и виртуальный метод. Это как пример, при решении других задач могут быть еще какие то приемы оптимизации.
Posted via RSDN NNTP Server 2.0
--
Справедливость выше закона. А человечность выше справедливости.
Re[3]: Саморегистрация классов в фабрике
От:
Аноним
Дата:
18.03.06 19:49
Оценка:
Здравствуйте, rg45, Вы писали:
R>" Аноним " <0@users.rsdn.ru> сообщил/сообщила в новостях следующее: news:1789146@news.rsdn.ru... >> Так это, с чего всё начиналось - >> да, switch'а нет, зато есть std::map, что в плане производительности ещё хуже
R>ИМХО в данной, весьма общей, постановке задачи больший интерес представляет построение максимально универсального решения, а не быстрого и специализированного. Тем более, что во многих случаях будет возможность оптимизацию по скорости сделать дополнительной примочкой к общему решению. Например можно реализовать автоматическое присвоение числовых идентификаторов классов в ран тайме, если строить дополнительный статический мап для перекодировки строки в число. А доступ к идентификатору предоставлять через статичекскую переменную и виртуальный метод. Это как пример, при решении других задач могут быть еще какие то приемы оптимизации.
Каким образом ваше "максимально универсальное решение" позволит создавать такие классы у которохых разные конструкторы, но все есс-но потомки IHeader?
фабрика в зависимости от параметра {1,2,3} должна создать классы {Header1,Header2,Header3} соответственно. Как в эту фабрику передать аргументы конструкта, причем, разных конструкторов??
" Аноним " <0@users.rsdn.ru> сообщил/сообщила в новостях следующее: news:1790791@news.rsdn.ru... > Каким образом ваше "максимально универсальное решение" позволит создавать такие классы у которохых разные конструкторы, но все есс-но потомки IHeader? > фабрика в зависимости от параметра {1,2,3} должна создать классы {Header1,Header2,Header3} соответственно. Как в эту фабрику передать аргументы конструкта, причем, разных конструкторов??
Да в принципе реализовать не сложно, если базовая абстракная фабрика имеет перегруженные операции create_object c соответствующими параметрами.
Только не совсем ясна практическая ценность этой задачи, и какой смысл это решение обобщать?
Posted via RSDN NNTP Server 2.0
--
Справедливость выше закона. А человечность выше справедливости.
Re[5]: Саморегистрация классов в фабрике
От:
Аноним
Дата:
18.03.06 20:39
Оценка:
Здравствуйте, rg45, Вы писали:
R>" Аноним " <0@users.rsdn.ru> сообщил/сообщила в новостях следующее: news:1790791@news.rsdn.ru... >> Каким образом ваше "максимально универсальное решение" позволит создавать такие классы у которохых разные конструкторы, но все есс-но потомки IHeader? >> фабрика в зависимости от параметра {1,2,3} должна создать классы {Header1,Header2,Header3} соответственно. Как в эту фабрику передать аргументы конструкта, причем, разных конструкторов??
R>Да в принципе реализовать не сложно, если базовая абстракная фабрика имеет перегруженные операции create_object c соответствующими параметрами. R>Только не совсем ясна практическая ценность этой задачи, и какой смысл это решение обобщать?
Практическая ценность очевидна, конструктор это единственная функция наследников, которая может иметь произвольное описание, тогда как интерфейс фиксирован, в том числе и список аргументов виртуальных методов. Т. е. через конструктор моежно настраивать поведение неследников от общего интерфейса (см. обсуждение ниже по ветке).
remark wrote: > > Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, > и реализации создаются фабрикой по некоторому значению (типу > реализации). В решении обеспечивается саморегистрация классов реализаций > интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, > и решение становиться максимально открытым для расширения. > Может кому будет полезно. Или кто выскажет конструктивные замечания.
Конструктивных замечаний одно: если фабрика не найдена, ты кидаешь
std::runtime_error. Конструктор объекта может выкинуть исключение того
же типа — конфуз. Лучше определить новый тип исключения для ненайденной
фабрики.
Из неконструктивных: задача тривиальна, код должен быть также прост и
ясен. Здесь же код концептуально тяжелый, надуманный. Фтопку...
Здравствуйте, Аноним, Вы писали:
А>Каким образом ваше "максимально универсальное решение" позволит создавать такие классы у которохых разные конструкторы, но все есс-но потомки IHeader?
Это абсурд! Если у тебя есть какие-то специфические параметры конструктора для какого-то конкретного класса MySpecificHeader, то зачем вообще нужна фабрика??? Возьми да создай объект MySpecificHeader!
А>фабрика в зависимости от параметра {1,2,3} должна создать классы {Header1,Header2,Header3} соответственно. Как в эту фабрику передать аргументы конструкта, причем, разных конструкторов??
Тут ты фактически ещё до вызова метода фабрики create() знаешь объект какого типа ты хочешь создать.
Ты не понимаешь смысл фабрики. Например: парсим XML: есть название элемента (которое определяет объект какого класса надо создать) и есть текстовое содержание этого элемента. Тогда мы это текстовое содержание передаём в конструктор всех объектов и соответственно в метод create() фабрики:
Каждая реализация интерфейса IElement в конструкторе вольна сделать со строкой ElementContent что угодно — оставить как текст, распарсить в int, распарсить в double или ещё что-то. Но всё это происходит уже вне фабрики.
В твоём решении ошибка в том, что ты начал выполнять какие-то специфические действия (выделять int, double, Ptr) слишком рано — до фабрики, до вызова create(). Тебе надо поставить эту специфику после фабрики — внести её в конструкторы конкретных реализаций. А до фабрики никак ничего не разделять на разные типы и т.д.
Здравствуйте, MaximE, Вы писали:
ME>remark wrote: >> >> Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, >> и реализации создаются фабрикой по некоторому значению (типу >> реализации). В решении обеспечивается саморегистрация классов реализаций >> интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, >> и решение становиться максимально открытым для расширения. >> Может кому будет полезно. Или кто выскажет конструктивные замечания.
ME>Конструктивных замечаний одно: если фабрика не найдена, ты кидаешь ME>std::runtime_error. Конструктор объекта может выкинуть исключение того ME>же типа — конфуз. Лучше определить новый тип исключения для ненайденной ME>фабрики.
Наверное, речь идёт не о ненайденной фабрике, а о ненайденном типе продукта.
Согласен. Я старался привести только идею. Я не старался довести до библиотечной реализации.
Если делать библиотечную реализацию, то я думаю надо не просто другой тип исключения бросать, надо делать поведение настраиваемое: кто-то хочет одно исключение, что-то другое, кто-то int3, кто-то вывод в журнал и т.д.
ME>Из неконструктивных: задача тривиальна, код должен быть также прост и ME>ясен. Здесь же код концептуально тяжелый, надуманный. Фтопку...
Задача не так тривиальна как кажется. Я решаю задачу не просто создания объектов. Фактически этим я приделываю к с++ метаинформацию (рефлекшн). И позволяю решать такие задачи как: перебрать все типы; найти типы, соотв. определённому критерию; проверить, есть ли нужный тип.
з.ы. как ты думаешь навскидку какого размера в Loki файл Factory.h? 1000 строк кода.
Здравствуйте, remark, Вы писали:
R>Это абсурд! Если у тебя есть какие-то специфические параметры конструктора для какого-то конкретного класса MySpecificHeader, то зачем вообще нужна фабрика??? Возьми да создай объект MySpecificHeader!
Т.е. таки старый добрый switch по индексу Вот и я о том же зачем вообще нужна фабрика?
А>>фабрика в зависимости от параметра {1,2,3} должна создать классы {Header1,Header2,Header3} соответственно. Как в эту фабрику передать аргументы конструкта, причем, разных конструкторов??
R>Тут ты фактически ещё до вызова метода фабрики create() знаешь объект какого типа ты хочешь создать. R>Ты не понимаешь смысл фабрики. Например: парсим XML: есть название элемента (которое определяет объект какого класса надо создать) и есть текстовое содержание этого элемента. Тогда мы это текстовое содержание передаём в конструктор всех объектов и соответственно в метод create() фабрики:
Я знаю только индекс, но по этому индексу я конечно могу определить с каким классом он отождествлен и switch'ем, перебирая индексы вызову соответствующий конструктор.
R>В твоём решении ошибка в том, что ты начал выполнять какие-то специфические действия (выделять int, double, Ptr) слишком рано — до фабрики, до вызова create(). Тебе надо поставить эту специфику после фабрики — внести её в конструкторы конкретных реализаций. А до фабрики никак ничего не разделять на разные типы и т.д.
Не очень понятно. Я имею уже конкретную иерархию, например, от IHeader. Затем пользователь в диалоге выбирает какой конкретно класс IHeader нужно создать, передачей в приложение некой переменной (обычно константа из enum'а), а приложение по этой константе создает нужный класс, какой параметр передать в конструктор каждого класса приложение знает само, без информации от пользователя.
В это случае фабрику использовать не получится, насколько я понимаю, только switch?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, remark, Вы писали:
R>>Это абсурд! Если у тебя есть какие-то специфические параметры конструктора для какого-то конкретного класса MySpecificHeader, то зачем вообще нужна фабрика??? Возьми да создай объект MySpecificHeader!
А>Т.е. таки старый добрый switch по индексу Вот и я о том же зачем вообще нужна фабрика?
Я лично объект известного типа создаю вот так:
MySpecificHeader h(i);
switch я тут не применяю.
А>>>фабрика в зависимости от параметра {1,2,3} должна создать классы {Header1,Header2,Header3} соответственно. Как в эту фабрику передать аргументы конструкта, причем, разных конструкторов??
R>>Тут ты фактически ещё до вызова метода фабрики create() знаешь объект какого типа ты хочешь создать. R>>Ты не понимаешь смысл фабрики. Например: парсим XML: есть название элемента (которое определяет объект какого класса надо создать) и есть текстовое содержание этого элемента. Тогда мы это текстовое содержание передаём в конструктор всех объектов и соответственно в метод create() фабрики:
А>Я знаю только индекс, но по этому индексу я конечно могу определить с каким классом он отождествлен и switch'ем, перебирая индексы вызову соответствующий конструктор.
R>>В твоём решении ошибка в том, что ты начал выполнять какие-то специфические действия (выделять int, double, Ptr) слишком рано — до фабрики, до вызова create(). Тебе надо поставить эту специфику после фабрики — внести её в конструкторы конкретных реализаций. А до фабрики никак ничего не разделять на разные типы и т.д.
А>Не очень понятно. Я имею уже конкретную иерархию, например, от IHeader. Затем пользователь в диалоге выбирает какой конкретно класс IHeader нужно создать, передачей в приложение некой переменной (обычно константа из enum'а), а приложение по этой константе создает нужный класс, какой параметр передать в конструктор каждого класса приложение знает само, без информации от пользователя. А>В это случае фабрику использовать не получится, насколько я понимаю, только switch?
Помедетируй над своей фразой "какой параметр передать в конструктор каждого класса приложение знает само, без информации от пользователя". Каким образом приложение это знает? Как это будет реализовано?
Ценность фабрики становиться нулевой, если у тебя перед вызовом create() стоит switch, который определяет типы параметров, которые надо передать каждому конкретному классу.
Если у тебя такая задача, то ты просто неправильно применяешь фабрику.
Первый вариант решения: использовать фабрику, основанную на клонах. Т.е. фабрика содержит в себе по одному уже созданному экземпляру каждого продукта. При создании она просто копирует этот объект. Никакие параметры в этом случае передавать не надо.
Второй вариант решения: использовать создающую функцию:
class Header1 : public Header<Header1>
{
...
IHeader::Ptr create();
...
};
Т.е. в каждом классе продукта должа быть функция, которая не принимает параметров и создаёт объект. HeaderWrapper<> будет использовать эти функции для создания продуктов в фабрике. Но в каждой конкретной функции create() ты волен производить любые вычисления и передавать в конструктор любой произвольный набор параметров.
Суть фабрики в том, что бы весь твой код приложения ничего не знал о конкретных классах продуктов. То есть вообще ничего не знал. Что бы ему было фиолетово, когда новые классы продуктов появляются, удаляются изменяются и т.д. Именно для этого нужен интерфейс класса продукта и фабрика. И тут своего рода должен быть интерфейс конструктора, хотя в интерфейсе IHeader его выразить и не удаётся, но его нужно поддерживать или с помощью того, что все конструкторы принимают одинаковый набор параметров или с помощью производящей функции или с помощью клонов или ещё как-то. Тогда и только тогда появляется смысл в использовании фабрики. Если ты не можешь обеспечить одинаковое конструирование, то тебе нет смысла даже смотреть в сторону фабрики.
Когда я строю решение на основе такой фабрики с саморегистрацией заголовочный файл с описанием конкретного продукта (Header1.h, Header2.h и т.д.) я включаю только в соответствующий cpp'шник (Header1.cpp, Header2.cpp и т.д.). Всё! Больше никуда! В один файл. think about it.
Здравствуйте, remark, Вы писали:
R>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>Может кому будет полезно. Или кто выскажет конструктивные замечания.
[кусь]
Долго пытался разобраться. Вроде бы разобрался . Замечания выскажу. Насчет конструктивности не уверен . В целом за идею спасибо. Полезно будет. R>В принципе тут можно уменьшить кол-во классов путём совмещения: например код из HeaderFactory можно поместить в IHeader. Тут так сказать всё разложено по полочкам.
Согласен. ИМХО фабрику лучше иметь отдельно.
R>Заполнение фабрики информацией о реализациях происходит во время инициализации глобальных объектов. Т.е. уже к main() она "константная".
Да мне бы и ран-тайм саморегистрация подошла бы. Иногда так бывает надо . Твой плюс в том, что у тебя "domain" саморегистрация (т.е. саморегистрация до вызова main ) автоматическая. R>Сразу предвижу замечания по поводу оверхеда. Возможно он есть тут.
Ага. Первое, что хотелось заорать — а можно без буст/локи/шмоки/traits-ов в темплейтах??? Ну почему использование этих новомодных библиотек/концепций/фич считается круто? Или "передовики объектно-ориентированной мысли" просто жить без этого не могут? Если можно, вырази мысль "более простым" языком. Лично я буст/локи не знаю совсем. Темплейты немного знаю, но не настолько, чтобы рожать строки подобно
которую понимал минут 10 . И trairs пока "не умею готовить" .
R>Конечно для тривиальных случаев делать так не стоит. Но для сложных, я считаю такое решение оправдано. Зато это создаёт некую инфраструктуру, с которой "пользовательские классы" становяться проще. Зато тут всё ортогонально и все классы и методы простые, короткие и понятные.
Согласен. У самого никогда руки не доходили до конца создать такую структуру. Надеюсь основную идею я понял и смогу сам упростить (имеется в виду в первую очередь выкинуть boost::тра-ля-ля и заменить чем-то более съедобным) для своих целей. Хотя если это сделает автор, буду ему благодарен. R>А с "более простыми решения" рука об руку идут switch'и, дублирование и другие прелести.
Ну про switch уже сказали: map::find выполняется дольше switch. Для тех, кто не в курсе: если вы думаете, что switch — это последовательный if — то вы глубоко не правы. Современные оптимизирующие компиляторы стали довольно умными и научились превращать switch в поиск в отсортированном массиве, так что количество "итераций" порядка log2(N). Сию страшную тайну мне открыл MaximE. А учитывая, что сама "итерация" в случае с map длинее, то map::find в плане быстродействия проигрывает switch-у. Но идея от этого хуже не становится. В сложных случаях накладные расходы от map.find будут пренебрежимо малыми, а пользы от структурированности будет гораздо больше.
[кусь] R>... что-то уже до целой статьи дотягивает
R>
Здравствуйте, remark, Вы писали:
R>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>Может кому будет полезно. Или кто выскажет конструктивные замечания.
Не понравилось, что в реализации добавляются лишние данные(TYPE etc.). Лучше бы это вынести в отдельный класс, что-то типа typetraits
Здравствуйте, Константин Ленин, Вы писали:
КЛ>Здравствуйте, remark, Вы писали:
R>>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>>Может кому будет полезно. Или кто выскажет конструктивные замечания.
КЛ>Не понравилось, что в реализации добавляются лишние данные(TYPE etc.). Лучше бы это вынести в отдельный класс, что-то типа typetraits
Что-то типа этого
namespace HeaderDetails
{
template <typename T>
class HeaderTypeTraits{};
}
template<typename Derived>
struct Header : IHeader
{
private:
struct Initializer
{
Initializer()
{
// Саморегистрация
HeaderFactory::add( HeaderTypeTraits<Derived>::TYPECODE , IHeaderWrapper::Ptr(new HeaderWrapper<Derived>));
}
};
static Initializer initializer;
protected:
~Header()
{
// Надо обязательно заюзать эту переменную
(void)initializer;
}
};
class RealHeader : public ...
namespace HeaderDetails
{
template <>
class HeaderTypeTraits<RealHeader>
{
static const int TYPECODE = 0;
};
}
А если к этому, как заметил _nn_, прикрутить еще и автоинкремент ID...
Только вот придется для каждого наследника писать специализацию HeaderTypeTraits, но зато не надо будет их нагружать ненужной информацией
R>>Заполнение фабрики информацией о реализациях происходит во время инициализации глобальных объектов. Т.е. уже к main() она "константная". P>Да мне бы и ран-тайм саморегистрация подошла бы. Иногда так бывает надо . Твой плюс в том, что у тебя "domain" саморегистрация (т.е. саморегистрация до вызова main ) автоматическая. R>>Сразу предвижу замечания по поводу оверхеда. Возможно он есть тут. P>Ага. Первое, что хотелось заорать — а можно без буст/локи/шмоки/traits-ов в темплейтах??? Ну почему использование этих новомодных библиотек/концепций/фич считается круто? Или "передовики объектно-ориентированной мысли" просто жить без этого не могут? Если можно, вырази мысль "более простым" языком. Лично я буст/локи не знаю совсем. Темплейты немного знаю, но не настолько, чтобы рожать строки подобно
Ну там из boost'а то всего лишь shared_ptr. Если не нравится, то можешь boost ::shared_ptr<Something> заменить на Something*. Только тогда надо будет объект потом вручную удалять.
Дело не в том, что модно или без них нельзя. Дело в том, что там реализовано очень много распростанённых вещей. Если без них, то просто придётся реализовывать это всё руками, причём оно будет выглядеть практически идентично с реализацией в boost. Спрашивается, а смысл?
Ну, например, зачем мы используем std::vector? А можно без него?
Можно, конечно, но только самому в конце концов придётся его реализовать... Лучше всё равно врядли получится...
P>которую понимал минут 10 . И trairs пока "не умею готовить" .
Это же всего лишь определение переменной
Тут ничего не поделать — это побочный эффект шаблонов, который мне тоже не очень нравится.
R>>Конечно для тривиальных случаев делать так не стоит. Но для сложных, я считаю такое решение оправдано. Зато это создаёт некую инфраструктуру, с которой "пользовательские классы" становяться проще. Зато тут всё ортогонально и все классы и методы простые, короткие и понятные. P>Согласен. У самого никогда руки не доходили до конца создать такую структуру. Надеюсь основную идею я понял и смогу сам упростить (имеется в виду в первую очередь выкинуть boost::тра-ля-ля и заменить чем-то более съедобным) для своих целей. Хотя если это сделает автор, буду ему благодарен. R>>А с "более простыми решения" рука об руку идут switch'и, дублирование и другие прелести.
P>Ну про switch уже сказали: map::find выполняется дольше switch. Для тех, кто не в курсе: если вы думаете, что switch — это последовательный if — то вы глубоко не правы. Современные оптимизирующие компиляторы стали довольно умными и научились превращать switch в поиск в отсортированном массиве, так что количество "итераций" порядка log2(N). Сию страшную тайну мне открыл MaximE. А учитывая, что сама "итерация" в случае с map длинее, то map::find в плане быстродействия проигрывает switch-у. Но идея от этого хуже не становится. В сложных случаях накладные расходы от map.find будут пренебрежимо малыми, а пользы от структурированности будет гораздо больше.
Я уже писал выше на эту тему: map тут просто для большей понятности и краткости. Фабрику можно элементарно переделать на использование отсортированного vector'а, и использовать бинарный поиск. Тем более, что он заполняется до main().
Ещё к теме использования готовых библиотек: Такой отсортированный vector с интерфейсом map уже, кстати, есть в Loki, называется AssocVector
Т.е. можно просто заменить в описании std::map на Loki::AssocVector и получишь вычислительную сложность O(log2(N)).
Здравствуйте, Константин Ленин, Вы писали:
КЛ>Здравствуйте, remark, Вы писали:
R>>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>>Может кому будет полезно. Или кто выскажет конструктивные замечания.
КЛ>Не понравилось, что в реализации добавляются лишние данные(TYPE etc.). Лучше бы это вынести в отдельный класс, что-то типа typetraits
Согласен. Так можно сделать, у меня была такая мысль. Так:
class Header1 : public Header<Header1>
{
...
struct Meta
{
static const int TYPE = 1;
};
...
};
или так:
class Header1 : public Header<Header1>
{
...
};
templatе<>
struct Traits<Header1>
{
static const int TYPE = 1;
};
...
То, что это будет безоговорочно лучше, я бы не сказал. Писанины больше. Но зато более структурно.
Ну не знаю, кому как больше нравится.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Аноним, Вы писали:
R>>>Нет, там другое. R>>>Часть функциональности, о которой я говорю там есть. Но только часть, причём не главная. Там нет саморегистрации. А>>ИМХО, как раз основое (оно же главное) там есть. Добавлена только саморегистрация, что есть довольно сомнительное приобретение.
R>Ну нет же, совсем нет так. В Loki есть только фабрика. Да она хорошо и удобно сделана. Но это давно известная и так сказать простая вещь. Всё что она делает создаёт по идентификатору типа объект типа. И не более. R>Я же говорю, о более высокоуровневой функциональности, о метаклассах. Т.е. появляется возможность делать такие вещи как перебрать все типы, получить какие-то их свойства, получить название типа, которое можно вывести пользователю. И всё это удобно для пользователей инфраструктуры. Ничего даже рядом нет в Loki, там есть только создание объектов. R>Создание объектов, конечно, важная функциональноть. Но не главная. Если бы я решил только эту задачу, то я бы не стал заводить топик.
А>>В "классической" реализации фабрики классов есть возможность собрать вызовы Register в одном месте, что очень удобно при большом количестве "продуктов".
R>Вопрос спорный. То, о чём говоришь ты, как раз называют плохо расширяемым дизайном. Когда в одном месте много чего-то и это место надо постоянно править. А когда данные распределены и локализованы, это называют расширяемым дизайном. Добавил класс в одном месте и всё заработало. Не надо править в нескольких местах, не надо трогать старый отлаженный код, как предлагаешь ты.
Без автоинкремента фича саморегистрации бесполезна и даже опасна. Когда коды расбросаны по всему проекту и ты должен, как тут уже говорили, искать свободный ID...Тем более это хорошо, что ты вставляешь в мап insert'ом, если бы кто-нить вставлял operator[]'ом, можно было бы таких багов насажать с одинаковыми ID...
КЛ>Без автоинкремента фича саморегистрации бесполезна и даже опасна. Когда коды расбросаны по всему проекту и ты должен, как тут уже говорили, искать свободный ID...Тем более это хорошо, что ты вставляешь в мап insert'ом, если бы кто-нить вставлял operator[]'ом, можно было бы таких багов насажать с одинаковыми ID...
Абсолютно не согласен со всеми кто писал про автоинкремент идентификаторов. Либо вы чего-то не понимаете, либо я чего-то не понимаю.
Во-первых, для меня вообще туманно реальное использование этой фичи (приведите примеры).
С автоинкрементом: идентификаторы не постоянны, не известны, их нельзя никуда отправлять/принимать/хранить в постоянной памяти. Я вижу единственныую возможность их использовать: это пересылать объект из одной части программы в другую в сериализованном виде. Зачем это нужно? Можно передать сам объект.
Во-вторых, в инфрастуктуру вполне намеренно встроена проверка, которая выявит дублирование при первом запуске. Это сделано совсем не случайно и убирать это нельзя. Ну кто сделает без проверки, то сам молодец.
В-третьих, я уже тут писал, что во всех случаях, когда я использовал фабрики, ручное присвоение идентификаторов не просто можно, но и нужно. Ну просто без него нельзя.
Например: я парсю XML и пишу класс именно для элемента "person". Ну какое тут автоприсвоение идентификаторов?
Уже давно в БД используется формат данных, при котором если в поле XXX, лежит значение YYY, то это соответствует определённому классу. Ну какое тут автоприсвоение идентификаторов?
Если я поддерживаю какой-то протокол и в нём прописано, что пакету с таким-то типом соответствует код 47. Ну какое тут автоприсвоение идентификаторов?
Объясни мне ход мыслей, который приводит вас к мысли об автоприсвоении идентификаторов. Может я чего-то не понимаю.
Здравствуйте, Константин Ленин, Вы писали:
КЛ>Здравствуйте, Константин Ленин, Вы писали:
КЛ>>Здравствуйте, remark, Вы писали:
R>>>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>>>Может кому будет полезно. Или кто выскажет конструктивные замечания.
КЛ>>Не понравилось, что в реализации добавляются лишние данные(TYPE etc.). Лучше бы это вынести в отдельный класс, что-то типа typetraits
КЛ>Что-то типа этого
КЛ>
...
КЛ>
Когда я применял такую фабрику, сами классы реализаций были не очень большие и метаинформации о типе было не очень много (и того и того порядка несколько штук). Мне объявление этого всего в одном месте никаких проблем не доставляло, я бы даже сказал, что было удобно.
Возможно, если и реализация большая и метаинформации много, то имеет смысл их действительно разделить. Мне даже больше нравится вариант с вложенной структурой, который я привёл в следующем посте — так писать меньше.
КЛ>А если к этому, как заметил _nn_, прикрутить еще и автоинкремент ID...
"remark" <38267@users.rsdn.ru> сообщил/сообщила в новостях следующее: news:1791411@news.rsdn.ru... > Здравствуйте, Константин Ленин, Вы писали: > > > > Объясни мне ход мыслей, который приводит вас к мысли об автоприсвоении идентификаторов. Может я чего-то не понимаю. >
В начальном ведь примере эти идентификаторы подразумевались как некоторые идентификаторы протокола, и никто не говорил, что они должны идти попорядку.
Тебе в твоем примере нужно было использовать не 1,2,3 а совершенно призвольные идентификаторы, обозвав их типа: old_protocol, current protocol, advanced_protocol.
Тогда бы было меньше соблазнов прикручивать сюда автоинкремент.
А вообще сложность этого обсуждения, ИМХО, состоит в том, что каждый его участник рассматривает задачу в какой то специфической трактовке, в результате получается спор типа: "А что слаще — синее или толстое?"
Posted via RSDN NNTP Server 2.0
--
Справедливость выше закона. А человечность выше справедливости.