but sooner or later you would have to include all the files in order to store everything in a vector etc |
You actually wouldn't... because you only store the callback in the vector.. and the callback doesn't need to know about the class.
It's actually more work to do it this way... so if you're looking for less typing then this isn't the way to go. The main advantage to doing it this way is that it's easier to expand. You don't have to keep updating the factory every time you add a new type -- each type can add itself to the factory automatically.
Assuming you have a 'Parent' base class... and one or more 'Child' derived classes. The factory will generate the appropriate Child object depending on the ID it is given.
Right now you are probably doing something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
#include "spike.h"
#include "block.h"
// etc
Parent* factory(int id)
{
switch(id)
{
case id_spike: return new Spike;
case id_block: return new Block;
// etc
}
}
|
While this is direct... it requires adding a new #include and a new 'case' each time you create a new child class.
Instead... you can have the factory maintain a list of callbacks, where each callback constructs a different object. Since a callback is just a function, the factory doesn't need to actually generate the object itself (so it doesn't need to include the header), and instead it can just call the callback.
I'm going to use globals for the example since that's quick-n-dirty and I don't feel like coming up with an elaborate design for this. Also note this is pseudo-code and won't work as-is. Some of this will have to be placed in headers and stuff.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
// factory.cpp
#include <map>
#include <functional>
class Parent;
typedef std::function<Parent*()> callback; // our callback typedef
typedef std::map<int,callback> registry; // our 'registry'
// IE, each child class will add themselves to this and provide
// and provide a callback to generate their object
// this bit is important... to ensure 'theReg' is always constructed, make it static
// in a function -- do not make it global. If it is global, it is possible for other global
// objects to be created first ... which means some Child classes may add themselves
// to a map that has not been constructed yet!
registry& getFactoryRegistry()
{
static registry theReg;
return theReg;
}
// child classes will call this to register themselves
void addFactoryCallback(int id, callback clbk)
{
getFactoryRegistry()[id] = clbk;
}
// then, to actually generate the objects, just call the callback:
Parent* factory(int id)
{
callback c = getFactoryRegistry()[id]; // get the callback from the registry
return c(); // call the callback
}
|
Then... the child would be responsible for adding itself to the registry. This can be "automatic" by using a global object which registers itself in its constructor. To avoid copy/pasting this for every Child class, you can template it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
// in parent.h?
template <int ID, typename ChildType>
class AutoRegisterer // not the best name
{
public:
// the callback we will give to the factory:
Parent* generate()
{
return new ChildType;
}
// the constructor to automatically register this type
AutoRegisterer()
{
addFactoryCallback( ID, generate );
}
};
|
Once that is templated.... each child class needs only to add a single line to their cpp file:
1 2
|
// globally in spike.cpp
static AutoRegisterer< id_spike, Spike > burpfart;
|
1 2
|
// globally in block.cpp
static AutoRegisterer< id_block, Block > burpfart;
|
The name of the object doesn't matter because you'll never use it. I just picked "burpfart" because I'm tired and I thought it was funny.
By making this global object, it calls that object's ctor... which calls addFactoryCallback... which gives the factory a way to generate our child class.
So yeah. More complicated.
Also if you have any better ways I can handle this kind of thing I'm open for any ideas. |
Well I can't really hit the point exactly without seeing what the difference is between all these classes... but usually minor differences in tiles can be handled with simple attributes rather than polymorphism.
IE, you shouldn't need to create a separate 'Spike' and 'Ladder' class. Maybe just have a 'Tile' class that has spike/ladder attributes. Then you don't need a factory... and then you don't need to include a bunch of headers, because you'll only have 1.
But again -- hard to say for sure without seeing the classes in question.
EDIT: ninja'd by liuyang. We had the same idea.