General factory in C ++

I am working on a game and trying to implement a smart way to create npc objects in C ++ when parsing a text file.

This is currently hardcoded in the Factory object. Like this:

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+"'."; } 

This, in my opinion, is a very ugly way to do this. Since it (by the way) breaks the principle of Open / Closed .

I participate in Java, and in Java I would do something like each IActor reporting this class name and class type ActorFactory at the beginning of program execution. Then the factory will save the relation on the map and then can easily find which lines the object displays, and then it can easily instantiate it.

Edit: I would also like to be able to call a constructor with a variable number / type of arguments.

How to do it in C ++? Can this be done?

+2
c ++ factory
source share
4 answers

In C ++, you usually use the Abstract Factory design pattern.

The fact is that "the decision on the type of actor to be created should not be the responsibility of ActorFactory::create() ". In your case, this method should not determine which class should be created based on the string, but rather rely on the type; this type is the actual class of Factory.

  • Each actor class has its own Factory class: TrollFactory , DragonFactory , etc., based on the base class ActorFactory2 (completion 2, because ActoryFactory already completed);

  • Each specialized Factory class implements a virtual create() method without a parameter that returns a pointer to a newly created actor class;

  • If you need parameters for creating an actor, pass them to the Factory object before creating the actor: pass them to ctor and save them as member variables; create() will retrieve them later when creating the actor;

  • Thus, you can easily pass different arguments for different participants, and your Factory mechanism will be scalable (a step to the Open / Closed principle);

  • Now ActorFactory::create() takes a pointer to an object originating from ActorFactory2 and calls the ActorFactory2::create() method: it will create the requested actor with the corresponding arguments without the switch statement.

     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; } 
+3
source share

I answered in another question about C ++ factories. See there if interested in a flexible factory. I am trying to describe an old path from ET ++ in order to use macros that worked fine for me.

ET ++ was a project to port the old MacApp to C ++ and X11. In an attempt to do this, Eric Gamma, etc. Started thinking about Design Patterns

+1
source share

Specific term: a parameterized factory method and is part of the factory method design pattern.

To use a common factory, hold classes on a map and access through a string. If the class names are usable, register the class in factory with typeid (MyClass) .name () and return a copy of the class by providing the member function clone ().

However, for simple non-extensible factories, I use the approach from your question.

I cannot answer your question about passing more variable parameters, but for deserialization it is enough to pass a part to a class and let it deserialize itself (as you already think).

+1
source share

You can use the map to store function pointers that return Actor *, with a pointer to the object being created. so the code will be simple

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

(please excuse any possible freezes I made using function pointers, they are not my forte)

0
source share

All Articles