2010-12-05 3 views
9

Я пытаюсь создать абстрактный шаблон фабрики для нескольких абстрактных фабрик на C++ и придумал это.C++ Abstract Factory с использованием шаблонов

#define _CRTDBG_MAP_ALLOC 
#include <crtdbg.h> 
#include <map> 
#include <stdio.h> 

class Base 
{ 
public: 
    virtual ~Base() {} 

    virtual bool Get() = 0; 
}; 

class DerivedA : public Base 
{ 
public: 
    bool Get() 
    { 
     return true; 
    } 
}; 

class DerivedB : public Base 
{ 
public: 
    bool Get() 
    { 
     return false; 
    } 
}; 

template <class T> 
class Creator 
{ 
public: 
    virtual ~Creator(){} 
    virtual T* Create() = 0; 
}; 

template <class T> 
class DerivedCreator : public Creator<T> 
{ 
public: 
    T* Create() 
    { 
     return new T; 
    } 
}; 

template <class T, class Key> 
class Factory 
{ 
public: 
    void Register(Key Id, Creator<T>* Fn) 
    { 
     FunctionMap[Id] = Fn; 
    } 

    T* Create(Key Id) 
    { 
     return FunctionMap[Id]->Create(); 
    } 

    ~Factory() 
    { 
     std::map<Key, Creator<T>*>::iterator i = FunctionMap.begin(); 
     while (i != FunctionMap.end()) 
     { 
      delete (*i).second; 
      ++i; 
     } 
    } 
private: 
    std::map<Key, Creator<T>*> FunctionMap; 
}; 

int main(int argc, char** argv[]) 
{ 
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); 

    //Register 
    Factory<Base, char*> temp; 
    temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA>); 
    temp.Register("DB", (Creator<Base>*)new DerivedCreator<DerivedB>); 

    //Pointer to base interface 
    Base* pBase = 0; 

    //Create and call 
    pBase = temp.Create("DA"); 
    printf("DerivedA %u\n", pBase->Get()); 
    delete pBase; 

    //Create and call 
    pBase = temp.Create("DB"); 
    printf("DerivedB %u\n", pBase->Get()); 
    delete pBase; 

return 0; 
} 

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

temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA>); 

Я также интересуюсь строкой выше. Я смущен, почему я должен бросить. Я не очень хорошо разбираюсь в шаблонах, но я бы предположил, что он должен работать нормально, учитывая, что и класс шаблона, и фактический класс получены.

Этот код действительно работает нормально, как показано выше, и даже удаляет штраф без утечек памяти. Я просто не чувствую себя полностью комфортно.

Я не смог найти какие-либо реальные примеры шаблонных классов для этого, кроме как от MaNGOS (вау эмулятора) - https://mangos.svn.sourceforge.net/svnroot/mangos/trunk/src/framework/Dynamic/ObjectRegistry.h

Но я не думаю, что я могу использовать этот метод в моем проекте, потому что я планирую об использовании DLL в какой-то момент моего проекта и использует CRTP, который противоречит моему требованию полиморфизма времени выполнения.

+0

Да, линия вы вывесили это плохо. Между этими двумя типами нет никакой связи. Они специализируются на разных типах. Я также не уверен, почему вы вообще беспокоитесь о CRTP. Обычно он используется для * исключения * виртуальных функций. Но у вас все еще есть такие, так зачем беспокоиться о шаблонах? – jalf 2010-12-05 05:36:16

+0

Ну, что я пытаюсь сделать, это создать решение из 3 частей. Программы, библиотеки и библиотеки DLL. DLL будет содержать реализацию, Библиотека содержит фабрику, а программа использует интерфейс. Шаблон существует, потому что я буду делать это много. Я использую его для замены выбора драйвера текущего игрового движка. В настоящее время он имеет скопированный/вставленный код для видео, физики, ввода и аудио. – NtscCobalt 2010-12-05 05:53:24

ответ

10

Класс DerivedCreator<DerivedA> является Creator<DerivedA> не Creator<Base>.

Вы должны указать полученный шаблон, что базовый тип, поэтому он может реализовать интерфейс Creator<Base> путем создания экземпляра производного типа:

// DerivedCreator is Creator<BaseType> which creates a 
// DerivedType, not a Creator<DerivedType> 
template <class DerivedType, class BaseType> 
class DerivedCreator : public Creator<BaseType> 
{ 
public: 
    BaseType* Create() 
    { 
     return new DerivedType; 
    } 
}; 

// Register 
Factory<Base, std::string> temp; 
temp.Register("DA", new DerivedCreator<DerivedA, Base>); 
temp.Register("DB", new DerivedCreator<DerivedB, Base>); 

// or if you want to create lots with the same base: 
template <class DerivedType> 
class DerivedBaseCreator : public DerivedCreator<DerivedType, Base> {}; 

//Register 
Factory<Base, std::string> temp; 
temp.Register("DA", new DerivedBaseCreator<DerivedA>); 
temp.Register("DB", new DerivedBaseCreator<DerivedB>); 
0

Небольшие замечания, чтобы улучшить дизайн: 1) Использование shared_ptr вместо сырых указателей 2) использовать зЬй :: строку вместо обугленного *

Вы должны бросить, потому что типы Создателя, Творца и Создателя < DerivedB > являются совершенно разными типами. Способ исправить это, чтобы удалить слепки:

//Register 
Factory<Base, char*> temp; 
temp.Register("DA", new DerivedCreator<Base>); 
temp.Register("DB", new DerivedCreator<Base>); 
+2

Слегка подставляя исходные указатели для `shared_ptr` - довольно плохая идея. Вы не только рискуете получить круговые ссылки, но и связанные с ними утечки памяти, это также самый медленный вид умного указателя. Используйте интеллектуальные указатели, да. Но используйте «shared_ptr» только тогда, когда вам абсолютно необходимо совместное владение. – jalf 2010-12-05 14:06:14

+0

Да, я определенно согласен, что отход от char * - хорошая идея. Нашел, что один из трудных способов, когда я позвонил pBase = temp.Create («DA»); из отдельного адресного пространства, чем функция регистра. Я планирую использовать интеллектуальные указатели, но я лучше программирую без них, поэтому я добавлю их позже. – NtscCobalt 2010-12-05 18:53:50

-1

Вы можете сделать Factory :: Регистрация метод шаблона и использовать подталкивание MPL утверждают в

#include <boost/mpl/assert.hpp> 
#include <boost/type_traits.hpp> 

template <class T, class Key> 
class Factory 
{ 
public: 
/////////////////////////////////// 
template <typename _Base> 
void Register(Key Id, Creator<_Base> * Fn) 
{ 
    BOOST_MPL_ASSERT((boost::is_base_of<T, _Base>)); 

    FunctionMap[Id] = reinterpret_cast<Creator<T>*>(Fn); 
} 
/////////////////////////////////// 
//... 
}; 
Смежные вопросы