Solved

factory pattern

Posted on 2013-01-31
5
246 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 31

Accepted Solution

by:
Zoppo earned 150 total points
ID: 38844064
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
ID: 38845461
	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
ID: 38847660
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 33

Expert Comment

by:sarabande
ID: 38848788
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 31

Expert Comment

by:Zoppo
ID: 38850150
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

3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
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 use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

809 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