Pull to refresh

Comments 27

BaseBuilder'у очень не повредил бы виртуальный деструктор, а UnifiedFactory не плохо было бы сделать NonCopyable, всеж в нем есть поля указателей, хоть и в мапе…
Согласен, спасибо
Я тоже искал готовые решения, но не нашел. Поэтому сделал свою фабрику. Я старался сделать ее максимально гибкой, и без использования макросов. По умолчанию все обьекты регистрируется динамически во время выполнения, но никто не мешает сделать статический инициализатор.
Да, ваше решение практически идентичное. Это говорит о том что решение удачное.
Да уж, лучше фишками С++11 типа auto и decltype юзать, если уж так хочется сократить объем кода на кастах.
Подобные решения очень часто стоят рядом со стратегиями. Я писал нечто подобное, но плохо дружу с макросами, поэтому получалось не очень красиво… Взял макросы на заметку.
В кутиме аналогичный прием уже столет в обед юзается, но без этих страшных макросов.
в рантайме регистрация?
Естественно, но там фабрика не универсальная, а от того она значительно более простая и понятная.
~UnifiedFactory(){qDeleteAll(m_builders);}

Закатали бы лучше в QScopedPointer'ы, заодно бы получили невозможность копировать объект и автоматическое удаление. Ну или в случае если копирование нужно, тогда QSharedPointer
и накладные расходы на умный указатель. Нет, пусть будет попроще. По-поводу запрета копирования замечание принял.
Накладные расходы в фабрике? Это даже не на спичках экономия!
Профит сомнительный и читаемость хуже…
Ну не люблю я умные указатели юзать без острой необходимости :)
Что Вы будете делать в случае, если создаваемому объекту понадобится некий параметр при конструировании?
Как по мне, то регистрировать стоит не конечный класс, а его билдер, либо уже созданный объект класса с клонированием (прототип).

Еще, при необходимости, можно сделать шаблонный метод для конструирования классов конкретного типа (не базового). Это позволит в некоторых случаях избежать нежелательной конвертации к классу-наследнику.
Я использовал похожую конструкцию как IoC контейнер, но там у меня хранились объекты разных классов без общего предка (по принципу boost::any).
>>Что Вы будете делать в случае, если создаваемому объекту понадобится некий параметр при конструировании?

Не буду использовать данную фабрику :) вообще в таких случаях я вместо аргументов в конструкторе делаю некий init()

>>Еще, при необходимости, можно сделать шаблонный метод для конструирования классов конкретного типа (не базового)
Если вы в данном контексте знаете конкретный класс, зачем его конструировать через фабрику?
Если же этот класс сам является базовым для какого-то подможества — в данной реализации правильнее засунуть фабрику в него.
>>Если вы в данном контексте знаете конкретный класс, зачем его конструировать через фабрику?
Клиентский код может иметь доступ к декларации класса, но при этом не уметь его конструировать.

Конечно, все зависит от конкретной ситуации, я просто делал нечто подобное и вспомнил об этом.
Пример того, что я делал см. ниже.
>> Как по мне, то регистрировать стоит не конечный класс, а его билдер
Да, была такая мысль. Такое решение может быть гибче. Можно использовать ту же фабрику но с кастомными билдерами, а можно с шаблонными. В будущем если понадобится — так и сделаю.

>> либо уже созданный объект класса с клонированием (прототип)
И такой вариант реализован, но он имеет смысл для более узкого круга задач и более трудоёмок: необходима реализация клонирования в каждом наследнике и расход памяти больше.

Хочу привести пример IOC контейнера, о котором я упоминал:

class CContainer
{
    struct SortPred
    {
    public:
        bool operator ()(const boost::any& one, const boost::any& two)
        {
            return one.type().before(two.type()) ? true : false;
        }
    };
    std::set<boost::any, SortPred> m_objects;

public:
    template<typename T>
    void RegisterObject(T* pT)
    {
        m_objects.insert(boost::any(pT));
    }
    template<typename T>
    T* GetObject()const
    {
        T* pT = NULL;
        auto iter = m_objects.find(boost::any(pT));
        if (iter == m_objects.end())
        {
            return NULL;
        }
        return boost::any_cast<T*>(*iter);
    }
    template<typename T>
    void UnregisterObject(T* pT)
    {
        auto iter = m_objects.find(boost::any(pT));
        if (iter != m_objects.end())
        {
            m_objects.erase(iter);
        }
    }
};
UFO just landed and posted this here
1. Где вы вообще увидели MFC, и что за претензии к использованию C++11 в 2012 году?

2. std::set в примере, т.к. мне нужно было написать пример быстро и по сути (оригинальный код я показать не могу).
В оригинале у меня std::map с хитрым составным ключом, включающим в себя type_info + строчку.
Таким образом, что результатом поиска может быть только объект определенного типа (так же, как и в примере с std::set). Единственное отличие при использовании std::map это то, что я могу добавлять в контейнер любое количество объектов разного типа с нулевым ID (как и в примере), и несколько объектов одного типа с разными ID:
A* a = container.GetObject<A>();
B* b1 = container.GetObject<B>("b1");
B* b2 = container.GetObject<B>("b2");

В общем, оригинальный пример мне показался перегруженным и я сократил его до std::set.

3. type_info::berofre() возвращает int, а не bool.
UFO just landed and posted this here
Так, как ты мне тут тыкаешь, то с тобой мне точно больше говорить не о чем!
У вас статический объект для регистрации будет инициализирован для каждой единицы трансляции в которую включен заголовочный файл с макросом UF_REGISTER_DERIVED в результате регистрация может быть вызвана несколько раз.

чтобы избежать этого можно воспользоваться приемом который называется счетчик Шварца (Jerry Schwarz counter)

template<class T>
class AutoRegistrer
  {
  public:
    AutoRegistrer(const QString& name)
    {
      if (registered_++ == 0)
        factory()->registerClass<T>(name);
    }
  private:
    static int registered_;  
};

template<class T> int AutoRegistrer<T>::registered_= 0;

Совершенно верно. Но в моем решение повторная регистрация просто «затрёт» старую, никаких проблем не будет. Спасибо за интересное решение.
Sign up to leave a comment.

Articles