【问题标题】:Generic factory in C++ [duplicate]C ++中的通用工厂[重复]
【发布时间】:2010-12-17 00:06:56
【问题描述】:

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

目前这是在工厂对象中硬编码的。像这样:

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++ 中如何实现?能做到吗?

【问题讨论】:

    标签: c++ factory


    【解决方案1】:

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

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

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

    我无法回答您关于传递更多变量参数的问题,但要反序列化,只需将该部分传递给类并让它自行反序列化(您似乎已经这样做了)。

    【讨论】:

      【解决方案2】:

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

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

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

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

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

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

      【讨论】:

      • 这是有道理的,但会生成很多类(如果有很多演员)。
      • 优点是您可以将可变数量的参数传递给ctors。
      • 我没有看到工厂方法或抽象工厂模式之间有很多区别。因为在使用具体类之前,我们仍然需要了解它。就我而言,我需要根据配置文件中的名称实例化我的类。
      【解决方案3】:

      我已经回答了关于 C++ 工厂的另一个 SO 问题。如果对灵活工厂感兴趣,请参阅there。我尝试从 ET++ 中描述一种使用宏的旧方法,这对我来说非常有用。

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

      【讨论】:

      • 我会认为这有点骇人听闻。但它绝对是我想要的。
      【解决方案4】:

      您可以使用映射来存储返回 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);
      }
      

      (请原谅我对函数指针可能造成的任何错误,它们不是我的强项)

      【讨论】:

      • 感谢您的回复,但这不允许我有一个可变参数列表。 (参数数量不同,类型不同)
      猜你喜欢
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      • 1970-01-01
      • 2017-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多