2012-06-02 43 views
1

我有一個基類Duck,鴨類的工廠DuckFactory和一個鴨子池DucksPool具有抽象基類的類的工廠庫

目前代碼看起來像你可以看到下面。我想要的是將工廠註冊到池中,然後只需從池中請求新的鴨子(字符串,如您在main()中看到的那樣)。

然而,接下來的步驟是把Duck在鹼抽象類型與附加純虛方法getCapabilities(),並且對的Duck具有不同能力,其工廠可以與該池被註冊的子類的打,並且可以是在運行時通過池通過相應的工廠請求(並實例化)。

我已經嘗試了不同的事情,但每次嘗試後都碰到了牆壁。

你能告訴我如何實現我剛剛描述的clean C++ 03-only代碼? (不是C++ 11或類似的,它應該只編譯爲g++ -Wall file.cpp)。

沒有提升或類似的庫/框架,我想了解它是如何從「scratch」完成的。

只要滿足這些要求(管理並通過某些工廠從池中實例化鴨子),也歡迎其他設計方面的建議,並且可以通過「字符串名稱」實例化鴨子。

解決方案越乾淨越好。

#include <iostream> 
#include <string> 
#include <vector> 
#include <map> 

using namespace std; 

//--------------------------------------- 
class Duck { 
    Duck() { } 
public: 
    static Duck* newSelf() { return new Duck(); } 
    static char const* getName() { return "fooDuck"; } 
    void quack() { cout << "QUACK"; } 
}; 

template <class TDuck> class DucksFactory { 
    char const* friendly_name; 
public: 
    DucksFactory() : friendly_name(TDuck::getName()) { 
    } 
    TDuck* getNew() { return TDuck::newSelf(); } 
    char const* getName() const { return friendly_name; } 
}; 

class DucksPool { 
    map<string, DucksFactory<Duck>*> factories; 
public: 
    template <typename TDuck> void acknowledgeDuckType(DucksFactory<TDuck> *factory) { 
     factories[factory->getName()] = factory; 
    } 
    template <typename TDuck> void acknowledgeDuckFactory(void) { 
     DucksFactory<TDuck> *factory = new DucksFactory<TDuck>(); 
     factories[factory->getName()] = factory; 
    } 
    template <typename TDuck> TDuck* getNew(string type) { 
     return dynamic_cast<TDuck*>(factories[type]->getNew()); 
    } 
    Duck* getNew(string type) { 
     return factories[type]->getNew(); 
    } 
}; 

//--------------------------------------- 

int main(void) { 
    DucksPool *duckspool = new DucksPool(); 

    //DucksFactory<Duck> *enten_factory = new DucksFactory<Duck>(); 
    //duckspool->acknowledgeDuckType<Duck>(enten_factory); 
    duckspool->acknowledgeDuckFactory<Duck>(); 
    Duck *foo = duckspool->getNew("fooDuck"); 
    foo->quack(); 
    delete foo; 
    //delete enten_factory; 
    delete duckspool; 

    return 0; 
} 
+0

您可能意思是「C++ 03-only code」。 C++ 0x現在是C++,但編譯器還沒有趕上這個事實。 – aschepler

+0

@aschepler:是的,修好了 – Flavius

回答

1

這裏是我自己實現的模板工廠。你可以使用它並適應自我。沒有提升,沒有C++ 0x,只有STL - std :: map,std :: string。

用法示例:

#include "DynamicFactory.h" 
#include <iostream> 

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

    virtual const char * name() const = 0; 
    void quack() { std::cout << "QUACK"; } 
}; 

class FooDuck : public Duck 
{ 
public: 
    static const char * getName() { return "fooDuck"; } 
    virtual const char * name() const { return getName(); } 
}; 

class BarDuck : public Duck 
{ 
public: 
    static const char * getName() { return "barDuck"; } 
    virtual const char * name() const { return getName(); } 
}; 

int main() 
{ 
    typedef DynamicFactory<Duck> DuckFactory; // second template parametr Id is std::string by default 
    // you can use some enum or any else type as key of map 
    // typedef DynamicFactory<Duck, DuckEnum> DuckFactory - for example 
    DuckFactory factory; 
    factory.add<FooDuck>(FooDuck::getName()); 
    factory.add<BarDuck>(BarDuck::getName()); 

    std::auto_ptr<Duck> duck(factory.create(FooDuck::getName())); 
    std::cout << duck->name() << "\n"; 
    return 0; 
} 

DynamicFactory類:

#ifndef DYNAMICFACTORY_H_ 
#define DYNAMICFACTORY_H_ 

#include <map> 
#include <string> 
#include <memory> 

template <class Base> 
class AbstractInstantiator 
{ 
public: 
    AbstractInstantiator(){} 

    virtual ~AbstractInstantiator() {} 

    virtual Base* createInstance() const = 0; 

private: 
    AbstractInstantiator(const AbstractInstantiator&); 
    AbstractInstantiator& operator = (const AbstractInstantiator&); 
}; 


template <class C, class Base> 
class Instantiator: public AbstractInstantiator<Base> 
{ 
public: 
    Instantiator() {} 
    virtual ~Instantiator() {} 

    Base* createInstance() const 
    { 
     return new C; 
    } 
}; 

template <class Base, class Type> 
class DynamicFactoryIgnoreErrorPolicy 
{ 
public: 
    Base * classNotExist(const Type & type) const 
    { 
     return 0; 
    } 
    void classAlreadyRegistred(const Type & type) 
    { 
    } 
    void classNotRegistred(const Type & type) 
    { 
    } 
}; 

template <class Base, class IdType = std::string, template <class, class> class DynamicFactoryErrorPolicy = DynamicFactoryIgnoreErrorPolicy > 
class DynamicFactory : public DynamicFactoryErrorPolicy<Base, IdType> 
    /// A factory that creates objects by class name. 
{ 
public: 
    typedef AbstractInstantiator<Base> AbstractFactory; 
    typedef std::map<IdType, AbstractFactory*> FactoryMap; 
    typedef typename FactoryMap::iterator iterator; 

    DynamicFactory() {} 

    ~DynamicFactory() 
    { 
     for (typename FactoryMap::iterator it = _map.begin(); it != _map.end(); ++it)   
      delete it->second;   
    } 

    Base* create(const IdType & Id) 
    { 
     typename FactoryMap::const_iterator it = _map.find(Id); 
     if (it != _map.end()) 
      return it->second->createInstance(); 
     return classNotExist(Id); 
    } 

    Base * create(iterator & it) const 
    { 
     return it->second->createInstance(); 
    } 

    template <class C> 
    void add(const IdType& Id) 
    { 
     registerClass(Id, new Instantiator<C, Base>); 
    } 

    void registerClass(const IdType & Id, AbstractFactory* pAbstractFactory) 
    { 
     std::auto_ptr<AbstractFactory> ptr(pAbstractFactory); 
     typename FactoryMap::iterator it = _map.find(Id); 
     if (it == _map.end()) 
      _map[Id] = ptr.release(); 
     else 
      classAlreadyRegistred(Id); 
    } 

    void remove(const IdType & Id) 
    { 
     typename FactoryMap::iterator it = _map.find(Id); 
     if (it != _map.end()) 
     { 
      delete it->second; 
      _map.erase(it); 
     } 
     else 
      classNotRegistred(Id); 
    } 

    bool isExist(const IdType & Id) const 
    { 
     return _map.find(Id) != _map.end(); 
    } 

    size_t size() 
    { 
     return _map.size(); 
    } 

    iterator begin() { return _map.begin(); } 
    iterator end() { return _map.end(); } 

private: 
    DynamicFactory(const DynamicFactory&); 
    DynamicFactory& operator = (const DynamicFactory&); 

    FactoryMap _map; 
}; 


#endif 
+0

謝謝。使用我的g ++版本,它不能編譯,我必須修改它以分別讀取'this-> classAlreadyRegistred(Id);'和'return this-> classNotExist(Id);'。不過,很好的解決方案 – Flavius

+0

你能解釋一下'AbstractInstantiator'和'Instantiator'的基本原理嗎?我可以理解它做了什麼,但能夠聽到你的確很好地理解你爲什麼這樣做。 – Flavius

+0

AbstractInstantiator具有創建Base類的虛函數,因此只需要一個模板參數給Base。 Instantiator是從Base派生的創建者類C的具體實現。 因此,如果沒有AbstractInstantiator我們無法描述地圖容器,因爲只知道Base類型。 – Torsten

0

清潔代碼的一個必不可少的東西是被異常安全和清理資源(資源獲取就是初始化-RAII-成語) 。如果您想從頭開始編寫代碼,請確保將資源包裝在構造函數執行分配的類中,並且析構函數執行清理。

對於您的應用程序,資源是內存,並且您希望將指針分配給資源類中分配的內存。對於手動編碼的類模板SmartPointer<T>,您希望爲客戶端提供功能模板MakeSmartPointer<T>(),該函數在T的構造函數參數上進行重載。這樣,您可以消除代碼中的所有「裸」外觀newdelete

這是如何使你的代碼更清潔?那麼,如果你的代碼出於某種原因拋出了一個異常,那麼你所有資源類的析構函數將被調用,並且它們可以釋放你分配的資源(用於智能指針的內存)。

你想從頭開始編寫代碼太糟糕了,因爲從C++ 11起,標準庫有兩個有用的智能指針類:std::shared_ptrstd::unique_ptr。如果指向鴨子的指針可以由不同的客戶端共享,請使用shared_ptr,否則使用unique_ptr。 STL還提供std::make_shared<T>(),定義自己的make_unique<T>()(他們忘記包含它,但這可能會得到解決)相當容易。

順便說一句,你也應該完全隱藏DuckFactory從客戶端,因爲它是DuckPool的實現細節。或者甚至更好:使DuckPoolDuckFactory的實現細節。而且當前版本還有另一個內存泄漏(另一個是拋出異常時):DuckFactory已分配但從未釋放。通過智能指針,這是一個簡單易行的解決方案。