Sign in to follow this  
EvanElwood

Pluggable Factory Implementation

Recommended Posts

EvanElwood    122
Mason McCuskey wrote an article a long time ago for GameDev that described how to implement pluggable factories. I implemented something similar a long time ago based on his article for a project, but hadn't gone back to that code in ages. Today I was bored at work and decided that I had a need for that same functionality, but was tired of using macros or copy/paste coding to get them to work. Mason had mentioned something vague about using templates to provide the pluggable factory functionality but I had never really given it much thought. Anyway, since I had an hour left at work, I gave it a stab. Here's what I came up with:
#include <map>
#include <list>
#include <string>

// class to create all objects of base type T.

template<class T, class Key = std::string>
class Factory
{
protected:

	virtual T* make() const = 0;
	static std::map<Key, Factory<T, Key>*>* registry;

public:

	Factory( const Key& key )
	{
		// make sure the registry is constructed.
		if( registry == 0 )
		{
			registry = new std::map<Key, Factory<T, Key>*>;
		}

		// register this factory object.
		registry->insert( std::make_pair( key, this ) );
	}
	
	static std::list<Key> keys( void )
	{
		std::list<Key> allkeys; 

		if( registry )
		{
			for( std::map<Key, Factory<T, Key>*>::const_iterator j = registry->begin(); j != registry->end(); j++ )
			{
				allkeys.push_back( j->first );
			}
		}

		return allkeys;
	}

	static T* construct( const Key& key )
	{
		if( registry )
		{
			std::map<Key, Factory<T, Key>*>::const_iterator j = registry->find( key );

			if( j != registry->end() )
			{
				return j->second->make();
			}
		}

		return 0;
	}
};

// class to create the individual factory classes for base class B, subclass T.

template<class B, class T, class Key = string>
class FactoryMaker : public Factory<B, Key>
{
public:
	FactoryMaker( Key key ) : Factory<B, Key>( key ) {}
protected:
	B* make() const
	{
		return new T;
	}
};

template<class T, class Key> std::map<Key, Factory<T, Key>*>* Factory<T, Key>::registry = 0;



To use these things now couldn't be easier. Here's an example:
#include <stdio.h>
#include "Factory.h"

using namespace std;

// base class for loading files.
class Shape
{
public:
	virtual void display() {}
};

// subclass for rectangles
class Rectangle : public Shape
{
public:
	void display()
	{
		printf( "This is a rectangle.\n" );
	}
};

FactoryMaker<Shape, Rectangle> factory_instance_1("Rectangle");

// subclass for triangles.
class Triangle : public Shape
{
public:
	void display()
	{
		printf( "This is a triangle.\n" );
	}
};

FactoryMaker<Shape, Triangle> factory_instance_2("Triangle");

// subclass for hexagons.
class Hexagon : public Shape
{
public:
	void display()
	{
		printf( "This is a hexagon.\n" );
	}
};

FactoryMaker<Shape, Hexagon> factory_instance_3("Hexagon");

int main(int argc, char* argv[])
{
	list<string> names = Factory<Shape>::keys();

	printf( "Creating some shapes by name:\n" );

	// try to create a triangle just by specifying its name.
	Shape* myshape = Factory<Shape,string>::construct( "Triangle" );

	// try to display it.
	if( myshape ) myshape->display();

	// try to create a hexagon just by specifying its name.

	myshape = Factory<Shape>::construct( "Hexagon" );

	// try to display it.
	if( myshape ) myshape->display();

	// print out the names of all the registered Shape subclasses.

	printf( "All available Shape subclasses:\n" );

	for( list<string>::const_iterator j = names.begin(); j != names.end(); j++ )
	{
		printf( "Name: %s\n", j->c_str() );
	}

	return 0;
}



You'll see that I've set the default Key to type string, but it can be changed to whatever you use most.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this