Solved

factory pattern

Posted on 2013-01-31
5
244 Views
Last Modified: 2013-02-13
The typical factory approach utilizes a map where the key could be a string, int etc. and the value is a pointer to a derived type.

Then there's typically a create or register method that makes the assumption you know the 'key' up front.   I'd like a factory method, however I _dont_ know the types in advance so this idea of invoking a register or create method in advance doesnt apply.  

In my case I'll be given a unsigned char array with N elements.  From the array, I'll instantiate 1 of 20 possible types.

Ignoring the array for a minute and opting instead for an unsigned type.  My Create method would be akin to

  Object* Product ( unsigned int type ) {
     if ( type == 1 ) {
       return new  DerivedType1 ;
     } else if ( type == 2 ) {
       return new DerivedType2;
    } else if ( type == 3 ) {
       return new DerivedType3
    } else if ( type == 4 ) {
       return new DerivedType4 ;
    //etc
   } else {
     //oops
   }
  }


I dont want to create 20 types in advance using the canonical factory pattern.  Need sample code showing how to approach this.
0
Comment
Question by:forums_mp
5 Comments
 
LVL 30

Accepted Solution

by:
Zoppo earned 150 total points
Comment Utility
Hi forums_mp,

IMO there's no need to create any pointers to implement a factory which does what you need. Here you can find a sample code which allows you to register classes at any time and let the factory create instances:
template <class BASE_CLASS,typename KEY>
class ClassFactory
{
public:
	typedef BASE_CLASS*(*fnpCreateDerivedType)();
	typedef std::map < KEY, fnpCreateDerivedType > tCreatorMap;

	static tCreatorMap&		Creators()
	{
		static tCreatorMap inst;
		return inst;
	}

	template < class _CLASS >
	static void RegisterClass( const KEY& type )
	{
		Creators().insert( tCreatorMap::value_type( type, &CreateDerivedType<_CLASS> ) );
	}

	template < class _CLASS > static BASE_CLASS* CreateDerivedType()
	{
		return new _CLASS;
	}

	static BASE_CLASS* CreateDerivedType( const KEY& type )
	{
		tCreatorMap::iterator it = Creators().find( type );

		if ( Creators().end() == it )
		{
			// error, not a registered index
			return NULL;
		}

		return (*it->second)();
	}
};

class Object
{
public:
	virtual ~Object() {}
};

class DerivedType1 : public Object
{
public:
	DerivedType1()
	{
		std::cout << "DerivedType1 instance created" << std::endl;
	}
};

class DerivedType2 : public Object
{
public:
	DerivedType2()
	{
		std::cout << "DerivedType2 instance created" << std::endl;
	}
};

class DerivedType3 : public Object
{
public:
	DerivedType3()
	{
		std::cout << "DerivedType3 instance created" << std::endl;
	}
};

typedef ClassFactory< Object, unsigned int > ObjectFactory;
typedef ClassFactory< Object, std::string > ObjectFactoryStr;

Object* Product( const unsigned int& type )
{
	Object* obj = ObjectFactory::CreateDerivedType( type );

	if ( NULL == obj )
	{
		std::cout << "Error: no class registered for type " << type << std::endl;
	}

	return obj;
}

Object* Product( const std::string& type )
{
	Object* obj = ObjectFactoryStr::CreateDerivedType( type );

	if ( NULL == obj )
	{
		std::cout << "Error: no class registered for type " << type << std::endl;
	}

	return obj;
}

int _tmain(int argc, _TCHAR* argv[])
{
	Object* prod1, *prod2, *prod3;

	ObjectFactory::RegisterClass< DerivedType1 >( 1 );
	ObjectFactory::RegisterClass< DerivedType2 >( 2 );

	prod1 = Product( 1 );
	prod2 = Product( 2 );
	prod3 = Product( 3 );
	ObjectFactory::RegisterClass< DerivedType3 >( 3 );
	prod3 = Product( 3 );

	ObjectFactoryStr::RegisterClass< DerivedType1 >( "type1" );
	ObjectFactoryStr::RegisterClass< DerivedType2 >( "type2" );
	prod1 = Product( "type1" );
	prod2 = Product( "type2" );
	prod3 = Product( "type3" );
	ObjectFactoryStr::RegisterClass< DerivedType3 >( "type3" );
	prod3 = Product( "type3" );

	return -1;
}

Open in new window

This sample demonstrate use of either integer or string as key. It doesn't matter where/when classes are registered, as long as they are registered the factory can create them.

Hope that helps,

ZOPPO
0
 

Author Comment

by:forums_mp
Comment Utility
	ObjectFactory::RegisterClass< DerivedType1 >( 1 );
	ObjectFactory::RegisterClass< DerivedType2 >( 2 );

Open in new window


This is precisely what I dont want.  I don't want to register DerivedTypesN with a value in advance.   I'm given a 'character' array from which to determine DerivedType.  I'm starting to think I could do without a factory.   Perhaps something akin to the following might work for me.

BaseType *ptrX = ObjectFactory::DetermineDerivedType (  Array /*simulate within an int for simplicity*/ );  
ptrX->doWork ( .... )

Open in new window


Within the Determine method I may end up receving DerivedType1 or DerivedType2 or ...DeterivedTypeN
0
 
LVL 28

Expert Comment

by:pepr
Comment Utility
You can convert any decision proces into (say) type number -- as shown by ZOPPO. Your DetermineDerrivedType(x) can be created on the top of it.

The class-factory pattern means that the object of certain class is created based on the problem context. However, the classes have to be known in advance (at least in C++). Then determining the type number is a reasonable step to be used in decision process. Whatever complexity the decision process has, you have to finally decide the target type.
0
 
LVL 32

Expert Comment

by:sarabande
Comment Utility
i would like to support the answers of Zoppo and pepr. if you look at the initial if/else if "switch" of your initial post  you easily could see that calling appropriate "registering" function from a static function before would not add more or less functionality to the factory than it would with your create function.

static initialization however could register entries independently and automatically which makes the factory work bottom-up rather than top-down. that actually is the top argummment for the factory pattern that it could be advanced by any derived class with out changing the factory code or the baseclass. note if you registering function would return a bool for success you could use a static initialization for a dertived class like

// derived.h
...
class Derived : public Base
{
     static bool isregistered;
     ...
public:
     virtual Base* create() { return new Derived(); }
     ....
};

// derived.cpp
bool Derived::isregistered = BaseFactory::registerClass("Derived", new Derived());

Open in new window


(the above sample uses the class name for key and a sample pointer of the derived class to virtually create new instances).

note if your main purpose for creating pointers is the task to parse strings or messages for different inputs, a factory is not the right pattern. if the parsing has found a supported type you could also create an appropriate pointer at this point. there is no need for a factory to make things more complex as they have to be.

Sara
0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
Maybe my sample wasn't well chosen, it just did the same as the original code. But there's a difference, the shown code can even register classes with keys generated at runtime, i.e.:
if ( <any condition > )
{
	ObjectFactory::RegisterClass< DerivedType1 >( 1 );
}
else
{
	ObjectFactory::RegisterClass< DerivedType2 >( 1 );
}

Open in new window

or
int i = 0;
ObjectFactory::RegisterClass< DerivedType1 >( ++i );
if ( <any condition > )
{
	ObjectFactory::RegisterClass< DerivedType2 >( ++i );
}
ObjectFactory::RegisterClass< DerivedType3 >( ++i );

Open in new window

Another sample could be reading strings as keys from a file or someting.

ZOPPO
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now