我正在开发一款游戏,并试图通过解析文本文件来实现一种在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 ++中完成?可以吗?
答案 0 :(得分:3)
在C ++中,您通常会使用抽象工厂设计模式。
重点是:“关于要创建的角色类型的决定不应该是ActorFactory::create()
的责任。”在您的情况下,此方法不应该根据字符串决定实例化哪个类,而是依赖于类型;此类 是实际的工厂类。
每个actor类都有自己的工厂类:TrollFactory
,DragonFactory
等,从基类ActorFactory2
派生(尾随2,因为ActoryFactory
已经取);
每个专用工厂类实现一个虚拟create()
方法,不带参数返回指向新创建的actor类的指针;
如果你需要参数来构造一个actor,那么在创建actor之前将它们传递给工厂对象:在ctor中传递它们并将它们存储为成员变量; create()
稍后会在创建演员后检索它们;
通过这种方式,您可以轻松地为不同的参与者传递不同的参数,并且您的工厂机制将是可扩展的(向开放/封闭原则迈出一步);
现在,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);
}
(请原谅我用函数指针做的任何可能的搞砸,它们不是我的强项)