2009-11-16 84 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()成员函数返回该类的副本。

但是,为了简单,不可扩展工厂,我使用你的问题的方法

我无法回答你的问题关于传递更多的变量参数,但反序列化,它足以将该部分传递给类,让它反序列化自己(因为你已经似乎做)。