C ++中的通用工厂

时间:2009-11-16 09:57:26

标签: c++ factory

我正在开发一款游戏,并试图通过解析文本文件来实现一种在C ++中创建npc对象的智能方法。

目前,这是在Factory-object中进行硬编码的。像这样:

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 ++中完成?可以吗?

4 个答案:

答案 0 :(得分:3)

在C ++中,您通常会使用抽象工厂设计模式。

重点是:“关于要创建的角色类型的决定不应该是ActorFactory::create()的责任。”在您的情况下,此方法不应该根据字符串决定实例化哪个类,而是依赖于类型;此类 是实际的工厂类。

  1. 每个actor类都有自己的工厂类:TrollFactoryDragonFactory等,从基类ActorFactory2派生(尾随2,因为ActoryFactory已经取);

  2. 每个专用工厂类实现一个虚拟create()方法,不带参数返回指向新创建的actor类的指针;

  3. 如果你需要参数来构造一个actor,那么在创建actor之前将它们传递给工厂对象:在ctor中传递它们并将它们存储为成员变量; create()稍后会在创建演员后检索它们;

  4. 通过这种方式,您可以轻松地为不同的参与者传递不同的参数,并且您的工厂机制将是可扩展的(向开放/封闭原则迈出一步);

  5. 现在,ActorFactory::create()接受指向从ActorFactory2派生的对象的指针并调用ActorFactory2::create()方法:它将使用没有switch语句的适当参数创建请求的actor。 / p>

    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;
    }
    

答案 1 :(得分:1)

我在另一个关于C ++工厂的问题中回答过。如果您感兴趣的是灵活的工厂,请参阅there。我尝试用ET ++描述一种旧方法来使用对我来说很有用的宏。

ET++是一个将旧MacApp移植到C ++和X11的项目。为此,Eric Gamma等开始考虑设计模式

答案 2 :(得分:1)

具体术语是:参数化工厂方法,它是工厂方法设计模式的一部分。

要使用通用工厂,请在地图中保留类并通过字符串进行访问。如果您的类名可用,请使用“typeid(MyClass).name()将类注册到工厂,并通过提供clone()成员函数返回该类的副本。

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

我无法回答关于传递更多变量参数的问题,但是为了反序列化,只需将该部分传递给类并让它自行反序列化(就像你已经做过的那样)。

答案 3 :(得分:0)

您可以使用映射来存储返回Actor *的函数指针,其中指向正在创建的对象的指针。那么代码就是

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);
}

(请原谅我用函数指针做的任何可能的搞砸,它们不是我的强项)