2009-11-16 37 views
2

我正在開發一款遊戲,並試圖通過解析文本文件來實現一種智能方式,以C++創建npc對象。C++中的通用工廠

目前,這是硬編碼在一個工廠對象。像這樣:

IActor * ActorFactory::create(string actortype, Room * r, string name, int hp) 
{ 
    if(actortype == "Troll") 
    { 
     return new Troll(r, name, hp); 
    } 
    if (actortype == "Dragon") 
    { 
     return new Dragon(r, name, hp); 
    } 
    // ... and so on 
    throw "Can't recognize type '"+actortype+"'."; 
} 

這在我看來是非常醜陋的做法。由於它(除其他外)打破了Open/Closed principle

我受過教育的Java和Java中我會做一些喜歡有計劃開始執行每個IActor報告它的類名和類類型的ActorFactory。然後,工廠將關係存儲在地圖中,然後可以輕鬆查找哪些字符串映射到哪個對象,然後可以輕鬆實例化它。

編輯:我也想有能力調用具有可變數量/類型參數的構造函數。

怎麼會變成這樣在C++做了什麼?可以做到嗎?

回答

0

你可以使用地圖來存儲函數指針即會返回演員*,有了這樣一個指向對象被創建。所以上面的代碼就只是

std::map<std::string,IActor* (*) (Room*,std::string,int)> constructorMap  
constructorMap["Troll"]=&TrollConstructor 
//etc... 
IACtor* ActorFactory::create(string actortype,Room* r,string name,int hp){ 
    return (*constructorMap[actortype])(r,name,hp); 
} 

(請原諒任何可能的螺絲起坐我與函數指針做,他們不是我的強項)

+0

感謝您的答覆,但是這並不讓我有一個可變的參數列表。(不同量的參數和不同類型的) – 2009-11-16 10:15:54

3

在C++中,你通常會使用抽象工廠設計模式。

的觀點是:「關於演員創造不應該的ActorFactory::create()責任的類型決定的。」在你的情況下,這個方法不應該根據字符串來決定實例化哪個類,而應該依賴於一個類型;這種類型的的實際工廠類。

  1. 每個演員類都有其自己的工廠類:TrollFactoryDragonFactory等從基類ActorFactory2(尾隨2因爲ActoryFactory已被使用)導出;

  2. 每個專業廠家類實現無參數返回指針到一個新創建的演員類的虛擬create()方法;

  3. 如果你需要參數來構造一個actor,把它們傳遞給之前的工廠對象創建actor:將它們傳遞給ctor並將它們存儲爲成員變量; create()將在創建演員後稍後檢索它們;

  4. 這種方式,你可以很容易地通過不同的參數爲不同的角色,你的工廠機制會伸縮(朝開/關原理的步驟);

  5. 現在,ActorFactory::create()接受一個指向對象從ActorFactory2派生並調用ActorFactory2::create()方法:它會創建一個適當的參數要求的演員沒有switch語句。

    class ActorFactory2 
    { 
        string m_name; // Each IA actor has a name 
        int m_hp;  // and some HP 
    public: 
        ActorFactory2(const string &p_name, int p_hp) 
        : m_name(p_name), m_hp(p_hp) {} 
        virtual IActor * create() const = 0; 
    }; 
    
    class TrollFactory : public ActorFactory2 
    { 
        // No special argument needed for Troll 
    public: 
        TrollFactory(const string &p_name, int p_hp) 
        : ActorFactory2(p_name, p_hp) {} 
        virtual IActor * create() const { return new Troll(m_name, m_hp); } 
    }; 
    
    class DragonFactory : public ActorFactory2 
    { 
        FlameType m_flame; // Need to indicate type of flame a dragon spits 
    public: 
        DragonFactory(const string &p_name, int p_hp, const FlameType &p_flame) 
        : ActorFactory2(p_name, p_hp) 
        , m_flame(p_flame) {} 
        virtual IActor * create() const { return new Dragon(m_name, m_hp, m_flame); } 
    }; 
    
    IActor * ActorFactory::create(const ActorFactory2 *factory) 
    { 
        return factory->create(); 
    } 
    
    int main(int, char **) 
    { 
        ActorFactory af; 
        ... 
        // Create a dragon with a factory class instead of a string 
        ActorFactory2 *dragonFactory = new DragonFactory("Fred", 100, RedFlames); 
        IActor *actor = af.create(dragonFactory); // Instead of af.create("dragon", ...) 
        delete dragonFactory; 
    } 
    
+0

這是有道理的,但會產生大量的類(如果有很多演員)。 – 2009-11-16 17:42:29

+0

好處是您可以將可變數量的參數傳遞給ctors。 – 2009-11-19 08:16:35

+1

我沒有看到工廠方法或抽象工廠模式之間的很多差異。因爲在使用它之前我們仍然需要了解具體的類。在我的情況下,我需要在配置文件中的名稱上實例化我的類。 – 2013-01-28 02:46:11

1

我已經對C++的工廠另一SO回答過的問題。如果靈活的工廠感興趣,請參閱there。我嘗試從ET ++中描述一種老方法來使用對我來說很好的宏。

ET++是一個項目來港老MacApp C++和X11。在它的努力埃裏克伽馬等開始思考設計模式

+0

我會認爲這有點黑客。但它絕對正是我想要的。 – 2009-11-16 17:44:45

1

具體的術語是:參數化的工廠方法,它是工廠方法設計模式的一部分。

要使用通用的工廠,通過串保持在地圖和訪問類別。如果您的類名稱可用,請使用typeid(MyClass).name()將類註冊到工廠,並通過提供clone()成員函數返回該類的副本。

但是,爲了簡單,不可擴展工廠,我使用你的問題的方法

我無法回答你的問題關於傳遞更多的變量參數,但反序列化,它足以將該部分傳遞給類,讓它反序列化自己(因爲你已經似乎做)。