http://www.liamdevine.co.uk

Abstract Factory


Creative Commons License This work is licenced under a Creative Commons Licence.

[1] "Provide an interface for creating families of related or dependent objects without specifying their concrete classes."

I have seen various comments and questions on the abstract factory pattern, so I thought it time to actually use this site and introduce a version similar to one which I have been using for a while. Having read many articles and books on design patterns I really have no real idea where the influence came from; yet I seem™ to recollect that this class is based upon ideas drawn from [2] Andrei Alexandrescu's book.

There seems to be a need to explain what is happening in the workings of this class, as the design has been shown to people in the past with the result of them coming back and asking "So erm ... how does this class work again"? Yet once you understand, it is very simple.

Firstly lets brake it down:
Creator:   Creates a concrete class and returns a base class.
Table:   Mappings of key value pairs (Dictionary/Lookup Table).
Abstract factory:   Dictionary interface.

What does the [1] GOF quote say?
Provide an interface (Dictionary interface) for creating families of related or dependent objects (Mappings of key value pairs)without specifying their concrete classes.(Creates a concrete class and returns a base class).


The abstract_creator function is a very simple yet a powerful beast. NO really :)
What it allows the factory to do is store creator functions in a generic way, forgetting the concrete type as soon as it registered to the factory; in addition it never needs to see the header file for the type.
So is it all positive ?
Well, it really depends on how you want to use it. If for instance you are calling a default constructor then it is just the ticket you are looking for, otherwise well lets see how it handles that later on.
template<typename Base, typename Class>
inline std::auto_ptr<Base> abstract_creator()
{
	return std::auto_ptr<Base>(new Class);
}
For this instance and to keep to the current (pre 0x) C++ standard and not wondering into platform extensions, the table's container is an std::map which normally uses a Red and Black tree. Quick, but not as quick as a Hash map when using strings as keys.
So the table holds:
Key:   A key which can be string, enum ... which is a unique identification for a class in this table.
Value:   Function creator pointer, in other words a pointer to function taking no parameters and returning a base class auto ptr.
template<typename Base,typename Id>

class Abstract_factory
{
public:
	typedef std::auto_ptr<Base> (*func)();
...

typedef std::map< Id , func > Table;
typedef typename Table::iterator iter;

Table m_table;
...
Abstract Factory
Theres nothing really to this class, it's just an interface to upkeep the dictionary and call on values using keys into the table. So here is the source file and it is also inlined below, just so you can see how easy it is.

But what if we do not use the default constructor?
Well personally I have adapted to this class as it is very reusable, yet there is a method to do this. You can supply your own creation function as long as it meets the function signature of std::auto_ptr <Base> (*func)(). So if you really need to call a none default constructor, maybe to set a reference, then you could store the values before calling the create and calling into that inside the create function.
For example:
typedef some_type Bar;

//method to set bar
void set_bar(Bar f);
//method to get bar
Bar get_bar();

//structure constructor which takes a bar and sets a reference
struct Foo

{
	explicit Foo(Bar const& b):m_b(b){}

	Bar& m_b;
};

//creation function
template<typename Foo_base>
std::auto_ptr<Foo_base> create_foo()
{

	return std::auto_ptr<Foo_base>(new Foo( get_bar() );
}
///////////////////////////////////////////////////////////////////////////////
///  abstract_factory.h
///  Allows a user to register types and a key for the type(can be enums ints 
///  strings). There are two register functions one which takes a create 
///  function which must match the function signature:
///  std::auto_ptr<Base> (*func)()
///  this allows the user to be able to construct the object in the manner 
///  which they require . ie if the constructor for the class needs a certain
///  parameter then the user create function could pass it to the class,
///  via a function getter call or some other means. I have never used this functionality
///  of the class since its design :) Maybe I should just remove it ??
///  If a create function is not suppplied then the default will be used which 
///  just returns an auto pointer to the base class.
///
///  @License
///  This work is licenced under a Creative Commons Licence.
///  see:
///  http://creativecommons.org/licenses/by-nc-sa/3.0/
///  and:
///  http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode
///  For more details.
///  
///  @author Liam Devine
///  @email: see www.liamdevine.co.uk for contact details.
///////////////////////////////////////////////////////////////////////////////


#ifndef ABSTRCT_FACTORY_H_
#	define ABSTRCT_FACTORY_H_

#	include <map>
#	include <string>
#	include <algorithm>
#	include <memory>
#	include <stdexcept>


namespace LVD

{
	template<typename Base, typename Class>
		inline std::auto_ptr<Base> abstract_creator()
	{

		return std::auto_ptr<Base>(new Class);
	}

	template<typename Base,typename Id>

	class Abstract_factory
	{
	public:
		typedef std::auto_ptr<Base> (*func)();

		Abstract_factory(){}
		//search the map and look for the create function for this key, if found return the return value
		//of the function otherwise return null pointer 
		//LVD update: this does seem to be liked so instead it is throwing
		std::auto_ptr<Base> create(Id key)
		{

			iter i;
			//return ( ( i = m_table.find(key) ) != m_table.end() ) ? (i->second)() : std::auto_ptr<Base>(0);
			if(( i = m_table.find(key) ) != m_table.end() ){ return  i->second(); }

			else
			{
				throw std::runtime_error("could not find the key in the factory");
			}
		}
		//register a key and a create function for this key

		bool register_key(Id key, func function)
		{
			iter i;
			if ( ( i = m_table.find(key) ) == m_table.end() )
			{

				m_table[key] = function;
				return true;
			}
			else return false;
		}

		//register a key and supplies the type of class which this key relates to 
		//this is then used by the default creator to create the required class
		//when called
		template <typename Type> bool register_key(Id key)
		{
			iter i;

			if ( ( i = m_table.find(key) ) == m_table.end() )
			{

				m_table[key] = &abstract_creator<Base,Type>;
				return true;
			}

			else return false;
		}


		//remove a key and the create function from the map
		bool unregister_key(Id key)
		{

			iter i;
			if ( ( i = m_table.find(key) ) != m_table.end() )
			{

				m_table.erase(i);
				return true;
			}
			else return false;
		}

	private:
		typedef std::map< Id , func > Table;

		typedef typename Table::iterator iter;
		Table m_table;

	};

}


#endif//FACTORY_H_
[1] Erich Gamma et al, "Design Patterns: elements of reusable object-oriented software", Addison Wesley (1995)
[2] Andrei Alexandrescu , "Modern C++ Design: Generic Programming and Design Patterns Applied", Addison-Wesley (2001)

Files: abstract_factory.h


About Me | Site Map | Valid xhtml? | ©2006 - 2013 Liam Devine