Templatize your factories

Sunday, March 16, 2008

In Object Oriented Programming we create systems that manage objects. However, it is always a good idea to separate the implementation of the objects from the systems. This way, we can add or change objects and the system doesn't need to change.

This post is related to object Factories. A factory is responsible for creating objects. Here is a very simple example of an object factory:


IObject * ObjectFactory::CreateObject(const string & objectType)
{
    if (objectType == "Model")
        return new Model();
    else if (objectType == "Sound")
        return new Sound();
    else if (objectType == "Light")
        return new Light();

    return NULL;
}


Each of these 3 objects derive from the IObject interface. Based on the type of the object ("Model", "Sound", or "Light") we create that object (new Model(), etc).

This is great. But we can improve...

What if we want to get a list of object types that the factory is able to create? This was my question and here is the way I solved the problem.

First we create an interface for our object factory. This would allow us to create multiple object factories for different parts of an application (one for the map editor, one for the driving level, etc) or for different applications all together.


class IObjectFactory
{
    public:
        IObject *(*Creator)(void);

        virtual ~IObjectFactory() = 0 { }

        /**
        * Returns a list of objects that this factory can create
        **/
        list<string> GetCreationTypes(void);

        /**
        * Create an object of the type passed in
        **/
        IObject * CreateObject(const string & objectType);

    protected:
        void RegisterCreator(const std::string & objectType, IObjectFactory::Creator creator);

        void UnregisterCreator(const std::string & objectType);

    private:
        IObjectFactory::Creator * GetCreator(const std::string & objectType);

        map<string, IObjectFactory::Creator> objectCreators;
};


The concept is that we can Register a Creator into the IObjectFactory and then the factory will handle everything for us. We can create an object or get a list of object types that the factory can create at runtime without any additional modifications from us. The implementation for this interface is trivial...

This line is important:

typedef IObject *(*Creator)(void);


This defines a function pointer to a "Creator" function. This is a function that will create a specific object for us.

Here is a templated "Creator" for you to use:


template <typename T>
IObject * ObjectCreateFunction(void)
{
    return new T();
}


Then simply inherit from IObjectFactory in your concrete object factory and define a constructor similar to this:


ConcreateObjectFactory::ConcreateObjectFactory()
{
    RegisterCreator("Model", &ObjectCreateFunction<Model>);
    RegisterCreator("Sound", &ObjectCreateFunction<Sound>);
    RegisterCreator("Light", &ObjectCreateFunction<Light>);
}


P.S. I know that the IObjectFactory isn't a purely an interface but I am still going to call it an interface. Deal with it.

11 comments:

Chy said...

Then is this purely a way to organize the objects that are called on for messing with them in game modifications or does it make the game-play different? I don't completely understand the "factory" I think... that and I don't speak computer language and I can't talk to computers- so it's a bit foreign to me. :)

Brian Cronin said...

Think of a factory as a real world factory.

You build a shoe factory to create different kinds of shoes. You can tell the factory to make Red shoes or Green shoes or Blue shoes. The factory makes the shoes you ask for and out comes boxes of shoes. You don't even care what kind of shoes are in these boxes in the end. You ship them off to a retail store and they market, fit, and sell the shoes.

A system (shoe company) doesn't want to deal with each object (shoe ) in a different way. It wants to generalize their objects (shoe inventory) and let something else deal with the individual objects (shoes).

Gorion said...

Nice way of explaining an abstract factory pattern + some sort of singleton pattern.

But that is pretty handy way to solve such a thing (things you can create with the factory). Whenever i was lazy, i just hard coded that kind of stuff, but in the end, i had to do allot of editing :(

Looking forward to more posts like this, its interesting to see these kind of things :)

Gokz said...

Nice post, definetely interesting. Although, what is the point of creating a factory that creates :
A) Model
B) Sound
C) Lights
in keeping with your analogy thats like a company that makes
A) Jet Planes
B) Petroleum
C) Television sets
They are completely unrelated, why would you ever want to generalize those three into single factory...unless I'm missing something...

Brian Cronin said...

Gorion-
"Nice way of explaining an abstract factory pattern + some sort of singleton pattern."

My factories are actually not singletons. I instantiate a new factory (which always derives from an IObjectFactory) anytime I load a map (which is what my object factory is for) and pass the factory as a shared_ptr to the map for it to use during loading.

Gokz-
"They are completely unrelated, why would you ever want to generalize those three into single factory...unless I'm missing something..."

I do have those in a single factory. Plus many other things like a Sphere or Camera.

Basically it all has to do with the way my map format works. I have objects listed in the map that you want to spawn. These each have a type (Model, Sound, Light, etc) and a name. I have parameters listed for each object. These get passed into an object after it has been created by the factory. So the light will have a parameter called "DiffuseColor" while the sound has a parameter called "Volume", etc.

All the map cares about is that it is spawning and managing IObjects (which all 3 are). It doesn't care what they do or how they interact.

Thanks for the questions guys. I didn't feel like I covered everything I wanted to cover in the post. It was starting to get a bit long though...

Gokz said...

brian-
That makes a ton of sense. I definetely missed that way of looking at it. I was relating your factory system to a system that I have implemented in the game I am currently working on, so I was in the mindset that everything in the factory needed to be closely related or else there would be no real point. Your factory solution for loading things into the game is really cool though.

Anonymous said...

Did you get inspiration for this from anywhere?

Anonymous said...

Here you go this is the link I was thinking of.
http://www.liamdevine.co.uk/code/abstract_factory.php

Brian Cronin said...

That is a nice link, thanks anon!

I came up with my implementation independently. Inspiration came from the fact that I wanted to be able to retrieve a list of the object types my factory could create.

Anonymous said...

Hi Brian & David

I'm plan to make a game with my friend who is a great artist. I'm a programmer. I'm interested in how you guys meet and decided to create NimbleBit and if you guys plan to release ZeroGear as freeware or payware? In other words is NimbleBit a commercial company or are guys just doing it for fun?

Btw the graphics are great.

Brian Cronin said...

Anonymous (it would be nice if all the anonymous people would post some kind of name :) ),

We worked together at a couple companies and kinda just started working on it one day. Dave started on some art well before I started working on any code however. Then one day Dave put a website up at nimblebit.com and we were a "company".

We don't know how we want to release Zero Gear yet. There are a lot of options from Free to Play-Pay for Gear to Pay for the game one time to subscriptions, etc.

We want to be able to continue to support Zero Gear and make more awesome games so we are doing it for fun AND to make money.