Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения.
Может кому будет полезно. Или кто выскажет конструктивные замечания.
// Некий интерфейс, реализации которого будем создавать с помощью фабрикиstruct IHeader
{
typedef boost::shared_ptr<IHeader> Ptr;
virtual ~IHeader() = 0 {};
};
// Интерфейс метакласса реализации IHeaderstruct IHeaderWrapper
{
typedef boost::shared_ptr<IHeaderWrapper> Ptr;
virtual ~IHeaderWrapper() = 0 {};
virtual IHeader::Ptr create() = 0;
};
// Фабрика реализаций IHeaderclass HeaderFactory
{
private:
typedef std::map<int, IHeaderWrapper::Ptr> map;
static map& wrappers()
{
static map wrappers_instance;
return wrappers_instance;
}
public:
// Метод создания экземпляраstatic IHeader::Ptr create(int type)
{
map::iterator iter = wrappers().find(type);
if (wrappers().end() != iter)
return iter->second->create();
else
throw std::runtime_error("unknown header type");
}
// Метод добавления нового типа экземпляраstatic void add(int type, IHeaderWrapper::Ptr wrapper)
{
if (!wrappers().insert(std::make_pair(type, wrapper)).second)
throw std::logic_error("Header type added twice");
}
};
// Метакласс реализации IHeadertemplate<typename HeaderType>
struct HeaderWrapper : IHeaderWrapper
{
virtual IHeader::Ptr create()
{
return IHeader::Ptr(new HeaderType);
}
};
// Базовый класс для реализаций IHeader - обеспечивает саморегистрацию реализаций в фабрикеtemplate<typename Derived>
struct Header : IHeader
{
private:
struct Initializer
{
Initializer()
{
// Саморегистрация
HeaderFactory::add(Derived::TYPE, IHeaderWrapper::Ptr(new HeaderWrapper<Derived>));
}
};
static Initializer initializer;
protected:
~Header()
{
// Надо обязательно заюзать эту переменную
(void)initializer;
}
};
template<typename HeaderType> typename Header<HeaderType>::Initializer Header<HeaderType>::initializer;
// Реализация IHeaderstruct Header1 : Header<Header1>
{
static const int TYPE = 1;
// Реализация
};
// Реализация IHeaderstruct Header2 : Header<Header2>
{
static const int TYPE = 2;
// Реализация
};
// Реализация IHeaderstruct Header3 : Header<Header3>
{
static const int TYPE = 3;
// Реализация
};
int main()
{
IHeader::Ptr h1 = HeaderFactory::create(1);
IHeader::Ptr h2 = HeaderFactory::create(2);
IHeader::Ptr h3 = HeaderFactory::create(3);
IHeader::Ptr h4 = HeaderFactory::create(4); // Тут будет исключение
}
В принципе тут можно уменьшить кол-во классов путём совмещения: например код из HeaderFactory можно поместить в IHeader. Тут так сказать всё разложено по полочкам.
Заполнение фабрики информацией о реализациях происходит во время инициализации глобальных объектов. Т.е. уже к main() она "константная".
Сразу предвижу замечания по поводу оверхеда. Возможно он есть тут. Конечно для тривиальных случаев делать так не стоит. Но для сложных, я считаю такое решение оправдано. Зато это создаёт некую инфраструктуру, с которой "пользовательские классы" становяться проще. Зато тут всё ортогонально и все классы и методы простые, короткие и понятные. А с "более простыми решения" рука об руку идут switch'и, дублирование и другие прелести.
Плюс эту инфрастуктуру можно нагрузить ещё всякими дополнительными бонусами.
1. Например, в фабрику можно поместить метод:
// Фабрика реализаций IHeaderclass HeaderFactory
{
public:
// Проверка на существование типаstatic bool exists(int type)
{
return wrappers().end() != wrappers().find(type)
}
};
Например, загрузили из БД тип (число), сразу можно проверить, а правилное ли это число, есть ли у нас такой тип.
2. С каждой реализацией можно связать гораздо больше информации, чем просто тип
// Реализация IHeaderstruct Header1 : Header<Header1>
{
// Связываем с типом числовое значениеstatic const int TYPE = 1;
// Связываем с типом другой тип. Например, тип диалога, который может отобразить информацию о данной реализацииtypedef Header1Dlg DlgType;
// Связываем с типом некий флагstatic const bool IS_SIMPLE = true;
// Связываем с типом строку, которую мы можем выводить пользователюstatic const char* GetName()
{
return"SOME_HEADER";
}
// Реализация
};
Соответственно надо расширить IHeaderWrapper для поддержки этих значений.
3. В фабрику можно добавить метод для получения всех типов:
// Фабрика реализаций IHeaderclass HeaderFactory
{
public:
// Получить список всех метаклассовstatic std::vector<IHeaderWrapper::Ptr> getAllWrappers();
};
Имея всё это можно написать функцию, например, для заполнения комбобокса информацией о всех существующих типах + с каждым элементом комбобокса связать код этого типа.
Или функцию, которая выберет из всех типов типы, которые отвечают заданному критерию, например, у которых флаг IS_SIMPLE == true;
В общем можно много чего ещё полезного реализовать, причём реализовать так сказать "красиво".
Плюсы:
1. Практически нулевое дублирование, что важно для сопровождаемого кода
2. Инрастуктура функциональная, расширяемая и "умная"
3. Сами классы реализаций интерфейсов абсолютно самодостаточные, самоописываемые, т.е. высокая локальность данных и функций
4. Чтобы добавить новый класс реализации надо просто его описать и его поддержка автоматически появиться везде где она должна быть. Чем не может похвастаться традиционный подход — саму реализацию добавил, добавил её поддержку в фабрику, добавил её поддержку в интерфейс пользователя, а при загрузке из БД забыл.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Аноним, Вы писали:
А>>Здравствуйте, remark, Вы писали:
R>>>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>>>Может кому будет полезно. Или кто выскажет конструктивные замечания.
А>>См. Александреску "Современное проектирование...". ИМХО там реализация лучше. А в последних версиях Loki аффтар "творчески переработал" этот кусок.
R>Нет, там другое. R>Часть функциональности, о которой я говорю там есть. Но только часть, причём не главная. Там нет саморегистрации.
ИМХО, как раз основое (оно же главное) там есть. Добавлена только саморегистрация, что есть довольно сомнительное приобретение. В "классической" реализации фабрики классов есть возможность собрать вызовы Register в одном месте, что очень удобно при большом количестве "продуктов". В твоём же случае, когда тебе понадобиться добавить очередной продукт, придётся обшарить все модули с целью узнать следующее незанятое значение TYPE. А их может быть несколько больше, чем один. R>Там нет возможности нагружать эту инфроструктуру дополнительной функциоанльностью. То о чём я говорю — своего рода метаинформация или рефлекшн — и соответственно код на более высоком уровне.
Вот тут хотелось бы поподробнее. Смысл фабрики — сокрытие типов. Откуда возьмётся информация?
IHeader::Ptr h1 = HeaderFactory::create(48);
h1 ... что дальше? IS_SIMPLE? как?
R>Хотя, возможно, мой класс фабрики можно относледовать от класса Loki::Factory, что бы заюзать часть функциональности. ))))
R>Плюсы: R>1. Практически нулевое дублирование, что важно для сопровождаемого кода R>2. Инрастуктура функциональная, расширяемая и "умная"
Loki::Factory R>3. Сами классы реализаций интерфейсов абсолютно самодостаточные, самоописываемые, т.е. высокая локальность данных и R>функций
Это уж как их реализует пользователь этой фабрики. R>4. Чтобы добавить новый класс реализации надо просто его описать и его поддержка автоматически появиться везде где R>она должна быть. Чем не может похвастаться традиционный подход — саму реализацию добавил, добавил её поддержку в R>фабрику, добавил её поддержку в интерфейс пользователя, а при загрузке из БД забыл.
Про обратную сторону медали этого удобства я уже писал.
Резюмируя: к Loki::Factory добавлено две примочки, одна из которых может быть полезна при условии, что реализация продуктов сконцентрирована в очень небольшом числе модулей, а вторая просто бессмыслена.
Здравствуйте, Аноним, Вы писали:
А>Так это, с чего всё начиналось - А>да, switch'а нет, зато есть std::map, что в плане производительности ещё хуже
Ты, другой аноним, нашего ремарка не обижай.
Какая тебе производительность?
Библиотеки пишут для увеличения скорости разработки.
Лучше нарисуй-ка мне свой быстрый свитч для неинтегрального типа. Хоть для строки.
P.S. А уменя смайлики отвалились — вставляться не хочут 8(
remark wrote: > > Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, > и реализации создаются фабрикой по некоторому значению (типу > реализации). В решении обеспечивается саморегистрация классов реализаций > интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, > и решение становиться максимально открытым для расширения. > Может кому будет полезно. Или кто выскажет конструктивные замечания.
Конструктивных замечаний одно: если фабрика не найдена, ты кидаешь
std::runtime_error. Конструктор объекта может выкинуть исключение того
же типа — конфуз. Лучше определить новый тип исключения для ненайденной
фабрики.
Из неконструктивных: задача тривиальна, код должен быть также прост и
ясен. Здесь же код концептуально тяжелый, надуманный. Фтопку...
"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
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, remark, Вы писали:
R>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>Может кому будет полезно. Или кто выскажет конструктивные замечания.
R>
// <skip>
R>[ccode]
R>// Реализация IHeader
R>struct Header1 : Header<Header1>
R>{
R> // Связываем с типом числовое значение
R> static const int TYPE = 1;
R> // Связываем с типом другой тип. Например, тип диалога, который может отобразить информацию о данной реализации
R> typedef Header1Dlg DlgType;
R> // Связываем с типом некий флаг
R> static const bool IS_SIMPLE = true;
R> // Связываем с типом строку, которую мы можем выводить пользователю
R> static const char* GetName()
R> {
R> return"SOME_HEADER";
R> }
R> // Реализация
R>};
R>
Можно передавать через шаблон некоторые аргументы:
Бонус небольшой изврат :-D
R>2. Инрастуктура функциональная, расширяемая и "умная"
Пока не будут применятся в полную метапрограммирование на шаблонах и на макросах не будет полностью расширяемая R>3. Сами классы реализаций интерфейсов абсолютно самодостаточные, самоописываемые, т.е. высокая локальность данных и функций R>4. Чтобы добавить новый класс реализации надо просто его описать и его поддержка автоматически появиться везде где она должна быть. Чем не может похвастаться традиционный подход — саму реализацию добавил, добавил её поддержку в фабрику, добавил её поддержку в интерфейс пользователя, а при загрузке из БД забыл.
R>... что-то уже до целой статьи дотягивает
Приветствуется
R>
"remark" <38267@users.rsdn.ru> wrote in message news:1788774@news.rsdn.ru... > Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. > Может кому будет полезно. Или кто выскажет конструктивные замечания. >...
А что, мне нравится.
Конечно слабым местом является то, что идентификаторы нужно назначать в каждом типе руками и, похоже, применительно к тому примеру, от которого родился этот топик, от этого никуда не денешься: в потоке данных хранятся именно числа. А вот в общем случае, ключ мапа можно было бы сделать строковым и загонять туда typeid(Derived).name(), вот это вообще будет хорошее решение.
Posted via RSDN NNTP Server 1.9
--
Справедливость выше закона. А человечность выше справедливости.
Re[9]: Саморегистрация классов в фабрике
От:
Аноним
Дата:
21.03.06 18:52
Оценка:
Ну раз уж я тут...
Здравствуйте, BitField, Вы писали:
BF>Здравствуйте, remark, Вы писали:
R>>Мне на ум, исходя из моего опыта использования фабрик и вообще если подумать, приходят только варианты, где (1) это постоянство обязательно и где явное задание идентификатора по крайней мере очень желательно.
R>>Вот я и прошу уже вроде раз пятый людей. которые пишут про автоматическое назначение, привести примеры и более внятно аргументировать свою мысль. А то только и слышу "было бы круто", "такое часто нужно", а потом все как-то замолкают...
BF>Бывает, что и нужно. Полгода назад была у меня такая задачка.
[...]
Спорю на тонну бачей, что на самом деле задача была как раз не такая. А именно, вовсе не требующая решения в виде фабрики классов с автоинкрементом ид класса. (ибо таких) Выкатывай тз, готовь бабасы. ))
Не самое удачное решение предлагается в качестве доказательства полезности кривого подхода. Я не противник кривых подходов. Теория теорией, а на практике приходится делать разные стыдные вещи.
Тем не менее, эти нехорошие вещи не стоит использовать для демонстрации полезности изначально кривого механизма.
Почувствуй свою ответственность, наконец. Вот прочитает ремарк твой пост и уволит всю армию своих подчинённых нах ни за член собачий. Бо они не используют таких "решений". Ты их кормить будешь? ))
Re: Саморегистрация классов в фабрике
От:
Аноним
Дата:
17.03.06 11:37
Оценка:
Здравствуйте, remark, Вы писали:
R>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>Может кому будет полезно. Или кто выскажет конструктивные замечания.
См. Александреску "Современное проектирование...". ИМХО там реализация лучше. А в последних версиях Loki аффтар "творчески переработал" этот кусок.
Здравствуйте, DigitalGuru, Вы писали:
DG>Здравствуйте, remark, Вы писали:
R>>Может кому будет полезно. Или кто выскажет конструктивные замечания.
DG>Так и хочется написать: "Ниасилил, слишком многа классав"
R>"remark" <38267@users.rsdn.ru> wrote in message news:1788774@news.rsdn.ru... >> Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. >> Может кому будет полезно. Или кто выскажет конструктивные замечания. >>...
R>А что, мне нравится. R>Конечно слабым местом является то, что идентификаторы нужно назначать в каждом типе руками и, похоже, применительно к тому примеру, от которого родился этот топик, от этого никуда не денешься: в потоке данных хранятся именно числа. А вот в общем случае, ключ мапа можно было бы сделать строковым и загонять туда typeid(Derived).name(), вот это вообще будет хорошее решение.
Да, но это уже зависит от конкретных условий приложения. Если в приложении подгружаются dll-модули, то выражение & typeid(Derived) , скрее всего, будет иметь разное значение в разных модулях. И если задача стоит о сохранении абстрактных данных в поток(файл) с последующей загрузкой этих данных, возможно даже другим приложением, то тоже не пройдет такой подход. Со строкой, конечно же, медленнее, но зато круг решаемых задач шире.
Posted via RSDN NNTP Server 1.9
--
Справедливость выше закона. А человечность выше справедливости.
Re: Саморегистрация классов в фабрике
От:
Аноним
Дата:
17.03.06 13:33
Оценка:
Так это, с чего всё начиналось —
да, switch'а нет, зато есть std::map, что в плане производительности ещё хуже
Здравствуйте, remark, Вы писали:
R>Надеюсь хоть кто-то дочитает до конца
Дочитал... Полезное решение..
Вот только есть одна проблема, о которой стоит, наверно, вспомнить...
При реализации классов в статической библиотеке существует вероятность, что они будут таки проигнорированы линкером....
Идея авторегестрации — ссылка на конструктор статического объекта в деструкторе?
Как это объясняет стандарт?
R>"remark" А вот в общем случае, ключ мапа можно было бы сделать строковым и загонять туда typeid(Derived).name(), вот это вообще будет хорошее решение.
hash(typeid(Derived).name())
будет хорошим решением (реализацию hash для string взять из java)
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, remark, Вы писали:
R>>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>>Может кому будет полезно. Или кто выскажет конструктивные замечания.
А>См. Александреску "Современное проектирование...". ИМХО там реализация лучше. А в последних версиях Loki аффтар "творчески переработал" этот кусок.
Нет, там другое.
Часть функциональности, о которой я говорю там есть. Но только часть, причём не главная. Там нет саморегистрации. Там нет возможности нагружать эту инфроструктуру дополнительной функциоанльностью. То о чём я говорю — своего рода метаинформация или рефлекшн — и соответственно код на более высоком уровне.
Хотя, возможно, мой класс фабрики можно относледовать от класса Loki::Factory, что бы заюзать часть функциональности.
Здравствуйте, BitField, Вы писали:
BF>Здравствуйте, remark, Вы писали:
R>>Надеюсь хоть кто-то дочитает до конца
BF>Дочитал... Полезное решение..
BF>Вот только есть одна проблема, о которой стоит, наверно, вспомнить... BF>При реализации классов в статической библиотеке существует вероятность, что они будут таки проигнорированы линкером....
Можно поподробнее, о каких именно классах идёт речь?
Когда я использовал такое решение идентификаторы хранились или в БД или были связаны с внешними программами. В таком случае явное указание идентификатора не только можно, но и нужно.
Если идентификаторы используются только внутри программы и за её пределы ни во времни, ни в пространстве не выходят, но можно сделать автоикремент. По-моему самое логичное для него место — при регистрации в фабрике. Если они конечно не нужны в компайл-тайм.
Хотя нужны ли будут в таком случае вообще идентификаторы и фабрика? Надо смотреть.
R>>2. Инрастуктура функциональная, расширяемая и "умная" __>Пока не будут применятся в полную метапрограммирование на шаблонах и на макросах не будет полностью расширяемая
Ну шаблоны тут есть (Header<>, HeaderWrapper<>), в них можно помещать шаблонный код.
Для макросов пока как-то не нашлось применения
R>>... что-то уже до целой статьи дотягивает __>Приветствуется
Лень
Здравствуйте, Аноним, Вы писали:
А>Так это, с чего всё начиналось - А>да, switch'а нет, зато есть std::map, что в плане производительности ещё хуже
Это не совсем с того началось, о чём вы думаете. Это началось с вопроса о саморегистрации.
А если нужна высокая производительность в этом месте, то в фабрике можно использовать упорядоченный vector или сделать тот же switch.
А если у интерфейса IHeader есть несколько виртуальных методов, то это решение будет выигрывать по скорости у С-подхода, т.к. switch (или поиск в упорядоченном vector) будет выполняться только один раз, а не много раз.
R>"remark" <38267@users.rsdn.ru> wrote in message news:1788774@news.rsdn.ru... >> Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. >> Может кому будет полезно. Или кто выскажет конструктивные замечания. >>...
R>А что, мне нравится. R>Конечно слабым местом является то, что идентификаторы нужно назначать в каждом типе руками и, похоже, применительно к тому примеру, от которого родился этот топик, от этого никуда не денешься: в потоке данных хранятся именно числа. А вот в общем случае, ключ мапа можно было бы сделать строковым и загонять туда typeid(Derived).name(), вот это вообще будет хорошее решение.
Не думаю, что в общем случае это будет хорошее решение. Там уже ниже такие замуты пошли про typeid и hash...
По моему опыту обычно идентификаторы либо храняться в БД, либо в файле, либо приходят по сети, либо это названия элементов в XML. Т.е. явное указание идентификатора не минус, а плюс.
Явно его можно не указывать только в том случае, если он не выходит за пределы программы не в пространстве, ни во времени. Тогда стоит вопрос, а нужна ли будет в таком случае вообще фабрика и идентификаторы?
Обычна фабрика нужна, когда тип класса считывают из внешнего источника: БД/XML/socket. А если всё происходит внутри программы, то зачем нужна фабрика и идентификаторы типов???
Здравствуйте, remark, Вы писали:
R>Здравствуйте, _nn_, Вы писали:
__>>Можно передавать через шаблон некоторые аргументы: __>>Бонус небольшой изврат :-D __>>
R>Можно. Тут по вкусу. Принипиально ничего не поменяется. R>Имхо так более наглядно и явно: R>
R> // Связываем с типом числовое значение
R> static const int TYPE = 1;
R>
Возможно лучше будет функция.
Так можно будет во времени выполнения загружать идентификатор.
__>>Что насчет автоинкремента номера ? __>>Что-то вроде: __>>
R>Когда я использовал такое решение идентификаторы хранились или в БД или были связаны с внешними программами. В таком случае явное указание идентификатора не только можно, но и нужно. R>Если идентификаторы используются только внутри программы и за её пределы ни во времни, ни в пространстве не выходят, но можно сделать автоикремент. По-моему самое логичное для него место — при регистрации в фабрике. Если они конечно не нужны в компайл-тайм.
Значит нужно какое-то универсальное и красивое решение R>Хотя нужны ли будут в таком случае вообще идентификаторы и фабрика? Надо смотреть.
А как определять разные классы ? По typeid ?
Если можно то лучше без него
R>>>2. Инрастуктура функциональная, расширяемая и "умная" __>>Пока не будут применятся в полную метапрограммирование на шаблонах и на макросах не будет полностью расширяемая R>Ну шаблоны тут есть (Header<>, HeaderWrapper<>), в них можно помещать шаблонный код. R>Для макросов пока как-то не нашлось применения
Значит разработка еще не доросла до библиотечного уровня
R>>>... что-то уже до целой статьи дотягивает __>>Приветствуется R>Лень
Не аргумент
R>>> R>
Здравствуйте, remark, Вы писали:
R>Здравствуйте, BitField, Вы писали:
BF>>Вот только есть одна проблема, о которой стоит, наверно, вспомнить... BF>>При реализации классов в статической библиотеке существует вероятность, что они будут таки проигнорированы линкером....
R>Можно поподробнее, о каких именно классах идёт речь?
Фабрика и классы заголовков (Header1, Header2) находятся в статической библиотеке. Пусть, например, Header1 (все его ф-ции, vtbl, и регистратор) находятся в единице трансляции, после компиляции которой имеет Header1.o[bj]. Он помещается в статическую библиотеку. При линковке этой библиотеки к исполняемому файлу в случае, если ни один из символов, определенных в Header1.o[bj], не используется в других обьектных файлах, линкер имеет полное право игнорировать Header1.o[bj] целиком -- и саморегистрация Header1 не происходит.
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>Только не совсем ясна практическая ценность этой задачи, и какой смысл это решение обобщать?
Практическая ценность очевидна, конструктор это единственная функция наследников, которая может иметь произвольное описание, тогда как интерфейс фиксирован, в том числе и список аргументов виртуальных методов. Т. е. через конструктор моежно настраивать поведение неследников от общего интерфейса (см. обсуждение ниже по ветке).
Здравствуйте, Аноним, Вы писали:
А>Каким образом ваше "максимально универсальное решение" позволит создавать такие классы у которохых разные конструкторы, но все есс-но потомки 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...
__>Объявляем в классе TYPE и все работает, хочется через HeaderTraits и снова нет никаких проблем.
Даже не знаю, что сказать...
Если смотреть с т.з. решения задачки на с++, то прикольно.
А если серьёзно, то ИМХО чем больше выбора, тем хуже. Это на первый взгляд хорошо, что можно и так и так. Но на второй взгляд, в этом же во всём разбираться и это же всё поддерживать.
Такие решения применяются, например, в стандартной библиотеке. Например, iterator_traits. Но они там не от хорошей жизни. Не для того, что бы прикольно сделать. Они там от того, что для итератора-указателя (итератор c-массива) не получиться сделать у него члены с описанием свойств. Поэтому это вынести отдельно в traits. Или например, для встроенных типов по другому никак traits не сделаешь.
А если таких проблем нет, и все свойства можно описать прямо "не отходя от кассы", то зачем извращаться.
R>"remark" <38267@users.rsdn.ru> сообщил/сообщила в новостях следующее: news:1791411@news.rsdn.ru... >> Здравствуйте, Константин Ленин, Вы писали: >> >> >> >> Объясни мне ход мыслей, который приводит вас к мысли об автоприсвоении идентификаторов. Может я чего-то не понимаю. >>
R>В начальном ведь примере эти идентификаторы подразумевались как некоторые идентификаторы протокола, и никто не говорил, что они должны идти попорядку. R>Тебе в твоем примере нужно было использовать не 1,2,3 а совершенно призвольные идентификаторы, обозвав их типа: old_protocol, current protocol, advanced_protocol. R>Тогда бы было меньше соблазнов прикручивать сюда автоинкремент. R>А вообще сложность этого обсуждения, ИМХО, состоит в том, что каждый его участник рассматривает задачу в какой то специфической трактовке, в результате получается спор типа: "А что слаще — синее или толстое?"
Проблема, по-моему, в том, что никто из говоривших про автоикремент не подумал хорошо, перед тем как говорить. По крайней мере ещё никто не привёл вразумительного примера применения.
__>>Объявляем в классе TYPE и все работает, хочется через HeaderTraits и снова нет никаких проблем.
R>Даже не знаю, что сказать... R>Если смотреть с т.з. решения задачки на с++, то прикольно. R>А если серьёзно, то ИМХО чем больше выбора, тем хуже. Это на первый взгляд хорошо, что можно и так и так. Но на второй взгляд, в этом же во всём разбираться и это же всё поддерживать.
+1
R>Такие решения применяются, например, в стандартной библиотеке. Например, iterator_traits. Но они там не от хорошей жизни. Не для того, что бы прикольно сделать. Они там от того, что для итератора-указателя (итератор c-массива) не получиться сделать у него члены с описанием свойств. Поэтому это вынести отдельно в traits. Или например, для встроенных типов по другому никак traits не сделаешь.
Это из-за корявости языка. (Но это другая тема). R>А если таких проблем нет, и все свойства можно описать прямо "не отходя от кассы", то зачем извращаться.
Нравится некоторым
R>
Здравствуйте, remark, Вы писали:
R>Решение для типовой задачи, когда есть интерфейс, реализации интерфейса, и реализации создаются фабрикой по некоторому значению (типу реализации). В решении обеспечивается саморегистрация классов реализаций интерфейса в фабрике, т.о. отпадает необходимость в switch'е в фабрике, и решение становиться максимально открытым для расширения. R>Может кому будет полезно. Или кто выскажет конструктивные замечания.
R>protected: R> ~Header() R> { R> // Надо обязательно заюзать эту переменную R> (void)initializer; R> }
У меня (GC 3) работает только так:
protected:
~Header()
{
// Надо обязательно заюзать эту переменную
(void)initializer;
int i = 3;
i++;
}
. ОБЪЯСНИТЕ МНЕ ЧТО ВЫ ВООБЩЕ ИМЕЕТЕ В ВИДУ! Он нужен для автоматической раздачи ID, что гораздо безопаснее по сравнению с ручной. Однако если постоянность пары ID-тип должна быть всегда, то автоинкремент может быть не применим. И все, больше я ничего не имел ввиду.
. ОБЪЯСНИТЕ МНЕ ЧТО ВЫ ВООБЩЕ ИМЕЕТЕ В ВИДУ! Он нужен для автоматической раздачи ID, что гораздо безопаснее по сравнению с ручной. Однако если постоянность пары ID-тип должна быть всегда, то автоинкремент может быть не применим. И все, больше я ничего не имел ввиду.
Ну хорошо, она безопаснее, но имеет ли она хоть какую-то практическую ценность отличную от нуля? Вот какой меня сейчас мучает вопрос.
На сколько я понимаю сейчас, её практическая ценность равна нулю, и следовательно разговоры о её безопасности не имеют смысла.
. ОБЪЯСНИТЕ МНЕ ЧТО ВЫ ВООБЩЕ ИМЕЕТЕ В ВИДУ! Он нужен для автоматической раздачи ID, что гораздо безопаснее по сравнению с ручной. Однако если постоянность пары ID-тип должна быть всегда, то автоинкремент может быть не применим. И все, больше я ничего не имел ввиду.
R>Ну хорошо, она безопаснее, но имеет ли она хоть какую-то практическую ценность отличную от нуля? Вот какой меня сейчас мучает вопрос. R>На сколько я понимаю сейчас, её практическая ценность равна нулю, и следовательно разговоры о её безопасности не имеют смысла.
R>
Это почему? Далеко не каждой системе нужно постоянство пары ID-TYPE
Здравствуйте, Константин Ленин, Вы писали:
КЛ>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, Константин Ленин, Вы писали:
R>>>>По поводу этого я ответил здесь
. ОБЪЯСНИТЕ МНЕ ЧТО ВЫ ВООБЩЕ ИМЕЕТЕ В ВИДУ! Он нужен для автоматической раздачи ID, что гораздо безопаснее по сравнению с ручной. Однако если постоянность пары ID-тип должна быть всегда, то автоинкремент может быть не применим. И все, больше я ничего не имел ввиду.
R>>Ну хорошо, она безопаснее, но имеет ли она хоть какую-то практическую ценность отличную от нуля? Вот какой меня сейчас мучает вопрос. R>>На сколько я понимаю сейчас, её практическая ценность равна нулю, и следовательно разговоры о её безопасности не имеют смысла.
R>>
КЛ>Это почему? Далеко не каждой системе нужно постоянство пары ID-TYPE
Мне на ум, исходя из моего опыта использования фабрик и вообще если подумать, приходят только варианты, где (1) это постоянство обязательно и где явное задание идентификатора по крайней мере очень желательно.
Вот я и прошу уже вроде раз пятый людей. которые пишут про автоматическое назначение, привести примеры и более внятно аргументировать свою мысль. А то только и слышу "было бы круто", "такое часто нужно", а потом все как-то замолкают...
[...] А>>В твоём же случае, когда тебе понадобиться добавить очередной продукт, придётся обшарить все модули с целью узнать следующее незанятое значение TYPE. А их может быть несколько больше, чем один.
R>Это зависит от идентификатора класса. Если это например название элемента в XML-файле, то не придётся лазить, что бы найти следующий неиспользованный идентификатор. Потомучто это уже проектировщик решит, что элемент называется, например, "person". R>Или, если например, если идентификаторы связаны с каким-то протоколом. То тоже не придётся лазить, уже известно, что поддерживаем новый тип с кодом, например, 47.
Да-да, я совсем забыл, проектировщики имеют уникальную память и никогда не ошибаются, а ситуации, когда протокол 47 (элемент "person") уже используются абсурдны и смешны.
R>>>Там нет возможности нагружать эту инфроструктуру дополнительной функциоанльностью. То о чём я говорю — своего рода метаинформация или рефлекшн — и соответственно код на более высоком уровне. А>>Вот тут хотелось бы поподробнее. Смысл фабрики — сокрытие типов. Откуда возьмётся информация?
А>>
Какое это имеет отношение к фабрике классов?
Кстати, хотелось бы увидеть реализацию HeaderFactory::IsSimpleType(const std::string&)
R>Хотя, конечно, метод IsSimpleType() можно вынести в IHeader, а его реализацию в Header<>, тогда будет работать как ты хочешь.
При этом от твоих метаклассов не останется совсем ничего.
R>>>Хотя, возможно, мой класс фабрики можно относледовать от класса Loki::Factory, что бы заюзать часть функциональности. А>>)))))
R>Не понимаю, чего смешного
А вот
[...] А>>Резюмируя: к Loki::Factory добавлено две примочки, одна из которых может быть полезна при условии, что реализация продуктов сконцентрирована в очень небольшом числе модулей, а вторая просто бессмыслена. А>>)
R>Мне лично, она не кажется бессмысленной. Я применял такое решение на практике. И получал от него вполне практическую выгоду.
Повторюсь, хотелось бы увидеть реализацию HeaderFactory::IsSimpleType().
P.S.: Смайлики так и не заработали. Коды тоже отвалились.
P.P.S.: Автоинкремент идентификатора типа — полный бред. Тут я на удивление солидарен.
R>>Это зависит от идентификатора класса. Если это например название элемента в XML-файле, то не придётся лазить, что бы найти следующий неиспользованный идентификатор. Потомучто это уже проектировщик решит, что элемент называется, например, "person". R>>Или, если например, если идентификаторы связаны с каким-то протоколом. То тоже не придётся лазить, уже известно, что поддерживаем новый тип с кодом, например, 47. А>Да-да, я совсем забыл, проектировщики имеют уникальную память и никогда не ошибаются, а ситуации, когда протокол 47 (элемент "person") уже используются абсурдны и смешны.
Когда я применял такое решение. Для улутшения памяти разработчиков я сделал примерно так:
struct IHeader
{
...
// тут добавил развёрнутый комментарий по поводу назначения/изменения кодовenum TYPE
{
TYPE1 = 1,
TYPE2 = 2,
TYPE3 = 3
};
...
};
struct Header1 : Header<Header1>
{
static const int TYPE = IHeader::TYPE1;
};
с++ всё таки достаточно мощный язык для решения подобного рода проблем.
R>>>>Там нет возможности нагружать эту инфроструктуру дополнительной функциоанльностью. То о чём я говорю — своего рода метаинформация или рефлекшн — и соответственно код на более высоком уровне. А>>>Вот тут хотелось бы поподробнее. Смысл фабрики — сокрытие типов. Откуда возьмётся информация?
А>>>
К фабрике классов никакого. Но это имеет отношение к той инфраструктуре, которую я предствил в исходном посте, и которая представляет из себя фабрику классов совмещённую неким рефлекшном для классов-продуктов.
А>Кстати, хотелось бы увидеть реализацию HeaderFactory::IsSimpleType(const std::string&)
Я так понимаю, что тут опечатка, и вместо const std::string& должен быть int?
Если быть полностью последовательным, то так:
Здравствуйте, remark, Вы писали:
R>Мне на ум, исходя из моего опыта использования фабрик и вообще если подумать, приходят только варианты, где (1) это постоянство обязательно и где явное задание идентификатора по крайней мере очень желательно.
R>Вот я и прошу уже вроде раз пятый людей. которые пишут про автоматическое назначение, привести примеры и более внятно аргументировать свою мысль. А то только и слышу "было бы круто", "такое часто нужно", а потом все как-то замолкают...
Бывает, что и нужно. Полгода назад была у меня такая задачка. Фабрика создает как обьекты с известными id (которые жестко прошиты в протокол), так и другие объекты, которые используются при обработке и формировании результата, и соответственно не имеют реальных сlass-id.
Да, их можно и создавать напрямую (через new ClassName), но через фабрику было проще, однообразнее. Кроме того, фабрика являлась владельцем всех созданных ею обьектов, некоторые обьекты могли существовать только в единственном экземпляре и в добавок еще и самодельный rtti был.
Правда, ту либу я не считаю написанной "по рекомендациям лучших собаководов"
Здравствуйте, BitField, Вы писали:
BF>Здравствуйте, remark, Вы писали:
R>>Мне на ум, исходя из моего опыта использования фабрик и вообще если подумать, приходят только варианты, где (1) это постоянство обязательно и где явное задание идентификатора по крайней мере очень желательно.
R>>Вот я и прошу уже вроде раз пятый людей. которые пишут про автоматическое назначение, привести примеры и более внятно аргументировать свою мысль. А то только и слышу "было бы круто", "такое часто нужно", а потом все как-то замолкают...
BF>Бывает, что и нужно. Полгода назад была у меня такая задачка. Фабрика создает как обьекты с известными id (которые жестко прошиты в протокол), так и другие объекты, которые используются при обработке и формировании результата, и соответственно не имеют реальных сlass-id. BF>Да, их можно и создавать напрямую (через new ClassName), но через фабрику было проще, однообразнее. Кроме того, фабрика являлась владельцем всех созданных ею обьектов, некоторые обьекты могли существовать только в единственном экземпляре и в добавок еще и самодельный rtti был. BF>Правда, ту либу я не считаю написанной "по рекомендациям лучших собаководов"
Ну это хотя бы первое упоминание о реальном применении
А как назначались идентификаторы таким классам и как из можно было получить в коде, т.е.
Фабрика не является синглетоном и управляет временем жизни обьектов, являясь их владельцем.
Возможно, применение shared_ptr/intrusive_ptr было-бы выходом получше, но при непродолжнительных сессиях с малым числом создаваемых обьектов (до 5000) было удобнее уничтожать их одним махом.
Был синглетон-регистратор, который регистрировал классы
class Factory
{
public:
class Registrator
{
public:
int RegisterWithId(int id, ClassDesc desc);
int RegisterWithoutId(ClassDesc desc)
{
return RegisterWithId(NewId(), desc)
}
//...
};
...
}
структура ClassDesc содержала указатели на ф-ции создания, удаления и клонирования экземпляров класса.
У каждого класса было статическое поле int m_classId и static getter к нему. На них же строился велосипедный rtti.
Все это было завернуто в шаблоны и макросы типа DECL_ACTION_WITH[OUT]_ID / IMPL_ACTION
А вообще -- повторюсь -- та либа не была написана "по рекомендациям лучших собаководов"(самодельный rtti и type-switch в нескольких местах). Но наверное все-же была получше, чем та, на идеях которой была основана( 3000 строчные свитчи -- это нечто )
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Аноним, Вы писали:
R>>>Это зависит от идентификатора класса. Если это например название элемента в XML-файле, то не придётся лазить, что бы найти следующий неиспользованный идентификатор. Потомучто это уже проектировщик решит, что элемент называется, например, "person". R>>>Или, если например, если идентификаторы связаны с каким-то протоколом. То тоже не придётся лазить, уже известно, что поддерживаем новый тип с кодом, например, 47. А>>Да-да, я совсем забыл, проектировщики имеют уникальную память и никогда не ошибаются, а ситуации, когда протокол 47 (элемент "person") уже используются абсурдны и смешны.
R>Когда я применял такое решение. Для улутшения памяти разработчиков я сделал примерно так:
R>
R>struct IHeader
R>{
R>...
R>// тут добавил развёрнутый комментарий по поводу назначения/изменения кодов
R>enum TYPE
R>{
R> TYPE1 = 1,
R> TYPE2 = 2,
R> TYPE3 = 3
R>};
R>...
R>};
R>struct Header1 : Header<Header1>
R>{
R> static const int TYPE = IHeader::TYPE1;
R>};
R>
R>с++ всё таки достаточно мощный язык для решения подобного рода проблем.
Твоё сообщение от 18.03.06 20:14
8<----------------------
Вопрос спорный. То, о чём говоришь ты, как раз называют плохо расширяемым дизайном. Когда в одном месте много чего-то и это место надо постоянно править.
8<----------------------
Политика двойных стандартов?
Впрочем, я никогда не говорил, что саморегистрацию абсолютно бесполезная штука. Она-то как раз может быть полезна в ряде случаев. Далеко не всегда, но в ряде.
А>>Какое это имеет отношение к фабрике классов?
R>К фабрике классов никакого. Но это имеет отношение к той инфраструктуре, которую я предствил в исходном посте, и которая представляет из себя фабрику классов совмещённую неким рефлекшном для классов-продуктов.
Зачем плодить таких "совмещённых" монстров? Не лучше ли иметь мух отдельно от котлет?
А>>Кстати, хотелось бы увидеть реализацию HeaderFactory::IsSimpleType(const std::string&)
R>Я так понимаю, что тут опечатка, и вместо const std::string& должен быть int?
Отнюдь. Именно неинтегральный тип. Это проблема? Впрочем, забей, это не имеет значения.
[...]
Вроде прояснилось.
Насколько я понял, речь идёт о динамическом определении свойств типов по идентификатору типа.
Т.е. доступность информации о конкретном типе в рантайме.
Решение данной задачи ты интегрируешь в фабрику классов, которая создана для решения задачи сокрытия информации о конкретном типе.
В таком случае, не кажется ли тебе этот выбор несколько странным? Зачем из двух решений взаимноисключающих задач сооружать монструозную "инфраструктуру"?
Получилось что-то вроде телескопа, вмонтированного в белую трость для слепых. Бесполезно расширенная функциональность.
R>>struct IHeader
R>>{
R>>...
R>>// тут добавил развёрнутый комментарий по поводу назначения/изменения кодов
R>>enum TYPE
R>>{
R>> TYPE1 = 1,
R>> TYPE2 = 2,
R>> TYPE3 = 3
R>>};
R>>...
R>>};
R>>struct Header1 : Header<Header1>
R>>{
R>> static const int TYPE = IHeader::TYPE1;
R>>};
R>>
R>>с++ всё таки достаточно мощный язык для решения подобного рода проблем. А>Твоё сообщение от 18.03.06 20:14 А>8<---------------------- А>Вопрос спорный. То, о чём говоришь ты, как раз называют плохо расширяемым дизайном. Когда в одном месте много чего-то и это место надо постоянно править. А>8<---------------------- А>Политика двойных стандартов?
Ну вот именно, что вопрос спорный. Я лишь говорю, у каждой медали есть 2 стороны. Можно сделать так, можно сделать так. Так решишь одну проблему, так другую.
А>Впрочем, я никогда не говорил, что саморегистрацию абсолютно бесполезная штука. Она-то как раз может быть полезна в ряде случаев. Далеко не всегда, но в ряде.
R>>К фабрике классов никакого. Но это имеет отношение к той инфраструктуре, которую я предствил в исходном посте, и которая представляет из себя фабрику классов совмещённую неким рефлекшном для классов-продуктов. А>Зачем плодить таких "совмещённых" монстров? Не лучше ли иметь мух отдельно от котлет?
Мне кажется, что тут всё нормально. Основа обоих сервисов — хранение списка метаклассов — это база — на основе этого работает и фабрика и другие сервисы.
А>>>Кстати, хотелось бы увидеть реализацию HeaderFactory::IsSimpleType(const std::string&)
R>>Я так понимаю, что тут опечатка, и вместо const std::string& должен быть int? А>Отнюдь. Именно неинтегральный тип. Это проблема? Впрочем, забей, это не имеет значения.
Нет, проблемы это абсолютно не представляет. Просто в примере. который я привёл идентификаторы — числа.
А>[...] А>Вроде прояснилось. А>Насколько я понял, речь идёт о динамическом определении свойств типов по идентификатору типа. А>Т.е. доступность информации о конкретном типе в рантайме. А>Решение данной задачи ты интегрируешь в фабрику классов, которая создана для решения задачи сокрытия информации о конкретном типе. А>В таком случае, не кажется ли тебе этот выбор несколько странным? Зачем из двух решений взаимноисключающих задач сооружать монструозную "инфраструктуру"? А>Получилось что-то вроде телескопа, вмонтированного в белую трость для слепых. Бесполезно расширенная функциональность.
Не согласен. По-моему, всё гармонично. Я лично это применял. Получилось очень красиво и функционально, лаконично и с нулевым дублированием.
R>Ассоциировался потом с классом? Что бы его можно было использовать через ActionA::GetClassID() ?
int ActionA::m_classId = Factory::Registrator::GetInstance().RegisterWithId(actionA, NormalActionDesc<ActionA>());
//actionA -- из энума известных протоколов. int ActionB::m_classId = Factory::Registrator::GetInstance().RegisterWithoutId(NormalActionDesc<ActionB>());
Здравствуйте, Аноним, Вы писали:
А>Спорю на тонну бачей, что на самом деле задача была как раз не такая. А именно, вовсе не требующая решения в виде фабрики классов с автоинкрементом ид класса. (ибо таких) А>) Выкатывай тз, готовь бабасы. А>)))
Не могу, NDA
Обрати внимание, что существовали классы с известным ID (забитым в структуру протокола нижнего уровня) -- для их создания пригодна обычная фабрика без автоинкремента и со статическим массивом (даже не мапой, ибо id у таких классов < 256) указателей на креаторы.
А вот с определенными в библиотеке классы удобнее было работать именно так (пусть даже при создании иногда приходилось даункастить).
Из плюсов: однообразная работа со всеми классами, отсутсвие контроля за тх временем жизни -- ибо обьект живет, пока жива фабрика-владелец. Кроме того, защита от сохранения обьекта, созданного на стеке (сtor/dtor у всех классов, кроме абстрактного базового -- protected). Помимо всего прочего, примение фабрики с increment-id было вызвано использованием самодельного rtti. (вот это один из главных минусов той либы)
А>Не самое удачное решение предлагается в качестве доказательства полезности кривого подхода. Я не противник кривых подходов. Теория теорией, а на практике приходится делать разные стыдные вещи. А>Тем не менее, эти нехорошие вещи не стоит использовать для демонстрации полезности изначально кривого механизма.
Я не привожу свое решение "в качестве доказательства полезности". Я привожу его именно как пример использования фабрики с increment-id. О полезности такого подхода нужно судить в каждом конкретном случае...
А вообще -- согласен, кривовато. Вполне можно было попробовать обойтись и без фабрики с increment-id. Dозможно, другие решения были бы проще/полезнее/оптимальнее.
Короче прикол такой.
Есть у меня подобная фабрика в одном многомодульном проекте.
в модуле 1 сидит фабрика и базовые интерфейсы моих сервисных класссов.
в модуле 2 сидят реализации моих сервисных класссов. наружу торчат только задефайненые индексы. То есть у либы весь интерфейс выглядит примерно так:
"WoldemaR" <15414@users.rsdn.ru> сообщил/сообщила в новостях следующее: news:1812783@news.rsdn.ru... > Здравствуйте, remark, Вы писали: > > Короче прикол такой. > Есть у меня подобная фабрика в одном многомодульном проекте. > в модуле 1 сидит фабрика и базовые интерфейсы моих сервисных класссов. > в модуле 2 сидят реализации моих сервисных класссов. наружу торчат только задефайненые индексы. То есть у либы весь интерфейс выглядит примерно так: >
> И так далее... > Это что из подключаемого ашника торчит. > Сами классы регистрились в статических глобальных переменных сипипишника. > > Ясно дело, что в третий модуль они нифига не грузились > пришлось заюзать вызов формальной функции-болвана. > > Вот такой прикол.
Знаем, наступали.
Чтоб ничего не вызывать, можно применить такой прием: в ашнике объявляется класс в котором объявляется только конструктор по умолчанию. Тут же в ашнике объявляется нестатическая константа этого типа. Определение конструктора этого класса дается в cpp-шнике, в котором определены фабрики. Тогда стоит только включить заголовок в каком либо модуле другого проекта и линковка фабрик обеспечена.
Posted via RSDN NNTP Server 2.0
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Знаем, наступали. R>Чтоб ничего не вызывать, можно применить такой прием: в ашнике объявляется класс в котором объявляется только конструктор по умолчанию. Тут же в ашнике объявляется нестатическая константа этого типа. Определение конструктора этого класса дается в cpp-шнике, в котором определены фабрики. Тогда стоит только включить заголовок в каком либо модуле другого проекта и линковка фабрик обеспечена.
Здравствуйте, Oval, Вы писали:
O>Но в целом — супер! O>Выбросил Locki-фабрику нафик.
+ Когда избавился от Locki-фабрики пропала излишняя зависимость файлов проекта.
Мне кажется это надо запостить в comp.lang.c++
Здравствуйте, Oval, Вы писали:
O>Здравствуйте, Oval, Вы писали:
O>>Но в целом — супер! O>>Выбросил Locki-фабрику нафик. O>+ Когда избавился от Locki-фабрики пропала излишняя зависимость файлов проекта. O>Мне кажется это надо запостить в comp.lang.c++
Там на английском надо?
Если на английском, то если кому не лень может запостить... ну с небольшой ссылочкой на автора
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Oval, Вы писали:
O>>Здравствуйте, Oval, Вы писали:
O>>>Но в целом — супер! O>>>Выбросил Locki-фабрику нафик. O>>+ Когда избавился от Locki-фабрики пропала излишняя зависимость файлов проекта. O>>Мне кажется это надо запостить в comp.lang.c++
R>Там на английском надо? R>Если на английском, то если кому не лень может запостить... ну с небольшой ссылочкой на автора
Если выдавать эту фабрику как замену Loki::Factory, то надо доделать то, чего у меня ещё нет — параметры для конструктора. У меня в примере без параметров, а надо сделать: