#include <map>
#include <functional>
#include <exception>
#include <stdexcept>
#include <memory>
#include <iostream>
template<class TSrcType>
class TypeID : public std::unary_function<TSrcType, TSrcType>
{
public:
typedef TSrcType objTypeId;
};
template<class TKeyType, class TBaseType>
class TObjFactory
{
public:
typedef TBaseType * value_type;
TObjFactory(void) {}
~TObjFactory(void)
{
typename typeMapKeyToBuilder::iterator it(m_mapKeyToBuilder.begin()),itend(m_mapKeyToBuilder.end());
for(;it != itend; ++it)
delete it->second;
}
template<typename TSubType>
void registerBuilder(const TKeyType & key,TypeID<TSubType> obj)
{
typedef typename TypeID<TSubType>::objTypeId srcType;
typename typeMapKeyToBuilder::iterator it = m_mapKeyToBuilder.find(key);
if (it != m_mapKeyToBuilder.end())///need to allow for mutiple instance
throw std::runtime_error("duplicate");
m_mapKeyToBuilder[key] = new TObjBuilder<srcType>();
}
value_type buildObj(const TKeyType & key)
{
typename typeMapKeyToBuilder::iterator it = m_mapKeyToBuilder.find(key);
if (it == m_mapKeyToBuilder.end())
throw std::runtime_error("not found");
return it->second->buildObject();
}
protected:
class TObjBuilderBase
{
public:
virtual value_type buildObject(void) = 0;
};
template<class TSubType>
class TObjBuilder : public TObjBuilderBase
{
public:
virtual value_type buildObject(void)
{ return new TSubType(); }
};
typedef TObjBuilderBase * typeBuilderPtr;
typedef std::map<TKeyType,typeBuilderPtr> typeMapKeyToBuilder;
typeMapKeyToBuilder m_mapKeyToBuilder;
};
//---test---------------------
class Base
{
public:
virtual void test(void) = 0;
};
class classA : public Base
{
public:
virtual void test(void) { std::cout << 'A' << std::endl; }
};
class classB : public Base
{
public:
virtual void test(void) { std::cout << 'B' << std::endl; }
};
int main(void)
{
TObjFactory<int,Base> myFactory;
myFactory.registerBuilder(0,TypeID<classA>());
myFactory.registerBuilder(1,TypeID<classB>());
std::auto_ptr<Base> auA(myFactory.buildObj(0));
std::auto_ptr<Base> auB(myFactory.buildObj(1));
auA->test();
auB->test();
return 0;
}
+++++++++++++++++++++++++++++++++++++++++++++++++++
Introduction
Sometimes you need to create instances of classes in parts of an
application where you don't really need to know what the class details
are. You just want to create an instance.
This is often solved by
using factories. Constructions (in most cases these are classes) that
have the single purpose of simply creating other instances. This
article presents some different alternatives for writing such
factories. Each with their own advantages and disadvantages.
Factory 1: the simple factory
Example 1 contains a simple factory class.
class SimpleClassFactory
{
public:
SimpleClass *createInstance() {return new SimpleClass;}
};
This
class simply creates an instance of another class. This approach can be
used if the number of different classes is limited. You then only need
to write a limited number of class factories. The disadvantage of this
approach is that if you have many different classes, that you need to
write many factory classes as well. The following alternative tries to
solve this.
Factory 2: the multi-factory
Example 2 contains a factory
that is able to create different kinds of instances, depending on the
given arguments. Suppose that we have the following class hierarchy:
class SimpleBaseClass
{
public:
virtual int getValue() = 0;
virtual void setValue(int value) = 0;
};
class SimpleClass1 : public SimpleBaseClass
{
public:
int getValue() {return m_value;}
void setValue(int value) {m_value = value;}
private:
int m_value;
};
class SimpleClass2 : public SimpleBaseClass
{
public:
int getValue() {return m_value*100;}
void setValue(int value) {m_value = value;}
private:
int m_value;
};
A multi-factory implementation might look like this:
class SimpleClassFactory
{
public:
SimpleBaseClass *createInstance1() {return new SimpleClass1;}
SimpleBaseClass *createInstance2() {return new SimpleClass2;}
};
This
approach can be used if you have a central point that knows about all
the different classes that need to be instantiated, and the caller of
the factory knows which class he wants to instantiate. This
multi-factory cannot be used if the caller of the factory does now know
which classes could be generated by the factory and only passes some
magic value that needs to be dispatched to the correct instantiation.
Factory 3: the dispatching multi-factory
This approach is very similar to the previous one, but uses a dispatching method.
class SimpleClassFactory
{
public:
SimpleBaseClass *createInstance(int type);
};
SimpleBaseClass *SimpleClassFactory::createInstance (int type)
{
if (type==1) return new SimpleClass1;
else if (type==2) return new SimpleClass2;
else return NULL;
}
If one needs to create an instance of a certain type (1 or 2), it can create the instance like this:
SimpleBaseClass *simpleInstance = NULL;
SimpleClassFactory simpleClassFactory;
simpleInstance = simpleClassFactory.createInstance(1);
I admit that this is only a small difference with approach 2, but it can be useful in some situations.
Factory 4: the function-pointer-factory
The multi-factory has the disadvantage that the factory class needs
to know about all the different classes. It cannot be used in the
following situation:
Suppose that we have 10 classes. 5 of them are part of a module that
is used in multiple applications. 5 of them are application-specific.
They all inherit from the same base class. In some part of the
application we need to create an instance of one of these classes. The
exact class that needs to be instantiated depends on given value.
- We cannot write the factory in the shared part since it only knows about 5 of the classes.
- We don't want to write the factory in the application specific
part since that requires us to copy it over multiple applications AND
it can cause problems if a 6th shared class is added to the shared
module (some applications might forget to add it to the
application-specific-factory).
We could solve this problem by using function pointers, like this:
class SimpleClass1 : public SimpleBaseClass
{
public:
int getValue() {return m_value;}
void setValue(int value) {m_value = value;}
static SimpleBaseClass *createInstance() {return new SimpleClass1;}
private:
int m_value;
};
class SimpleClass2 : public SimpleBaseClass
{
public:
int getValue() {return m_value*100;}
void setValue(int value) {m_value = value;}
static SimpleBaseClass *createInstance() {return new SimpleClass2;}
private:
int m_value;
};
Each class is given a static '
createInstance()
'
method that creates an instance of the class. Our method that needs to
create an instance based on a given value, might look like this:
SimpleBaseClass *someMethod (
std::map<int,FactoryFunction> factoryFunctions, int type)
{
return factoryFunctions[type]();
}
The application can feed it with the supported factories like this:
std::map<int,FactoryFunction> factoryFunctions;
factoryFunctions[1] = &SimpleClass1::createInstance;
factoryFunctions[2] = &SimpleClass2::createInstance;
The method is then called like this:
simpleInstance = someMethod (factoryFunctions,1);
The
advantage is that it is simple to use and that it doesn't require a
factory per class or one big dispatching multi-factory that needs to
know about all the classes. Also the solution seems understandable for
developers with a pure C background. However, passing function pointers
in C++ is sometimes regarded as 'not done', and in the following
alternatives I'll give some other approaches.
Factory 5: the clone-factory
The clone factory approach makes
use of 'dummy' instances that are cloned. We need to give each class
that needs to be instantiated by someone a clone method, like this:
class SimpleBaseClass
{
public:
virtual int getValue() = 0;
virtual void setValue(int value) = 0;
virtual SimpleBaseClass *clone() = 0;
};
class SimpleClass1 : public SimpleBaseClass
{
public:
int getValue() {return m_value;}
void setValue(int value) {m_value = value;}
SimpleBaseClass *clone() {return new SimpleClass1(*this);}
private:
int m_value;
};
class SimpleClass2 : public SimpleBaseClass
{
public:
int getValue() {return m_value*100;}
void setValue(int value) {m_value = value;}
SimpleBaseClass *clone() {return new SimpleClass2(*this);}
private:
int m_value;
};
The method that needs to create the instances then needs a map of these 'dummy' instances instead of function pointers:
SimpleBaseClass *someMethod (
std::map<int,SimpleBaseClass *> clonables, int type)
{
return clonables[type]->clone();
}
And using it works like this:
SimpleBaseClass *simpleInstance = NULL;
SimpleClass1 clonable1;
SimpleClass2 clonable2;
std::map<int,SimpleBaseClass *> clonables;
clonables[1] = &clonable1;
clonables[2] = &clonable2;
simpleInstance = someMethod (clonables,1);
The
advantage of this approach is that we got rid of the function pointers.
However, the price we have to pay is that we need those dummy
instances. If the classes that need to be instantiated are simple (and
don't use excessive memory) that might be a solution. In other
situations where the class instances need lots of memory, or represent
more physical things (files, sockets, ...) it can be annoying to have
these dummy instances.
Factory 6: the template-factory
With this approach we try to write our factory using templates. First we foresee the following two template classes:
template <class BT>
class FactoryPlant
{
public:
FactoryPlant() {}
virtual ~FactoryPlant() {}
virtual BT *createInstance() = 0;
};
template <class BT,class ST>
class Factory : public FactoryPlant
{
public:
Factory() {}
virtual ~Factory() {}
virtual BT *createInstance() {return new ST;}
};
The
FactoryPlant
class
is the super class for the actual factories. In our previous examples,
all the classes that could be instantiated, all derived frm the same
base class. This is logical because otherwise it would be impossible to
write a 'generic' factory for these classes. The type given to the
FactoryPlant
is the base class of the instances that will be created by the factories inheriting from this
FactoryPlant
. The
Factory
class is given two types: the base class and the actual class that is instantiated.
As you can see the
FactoryPlant
base class already contains the pure virtual createInstance method. The
Factory
class
implements the method, since it knows which sub class type is actually
instantiated. We can now give our classes their own factory like this:
class SimpleClass1 : public SimpleBaseClass
{
public:
int getValue() {return m_value;}
void setValue(int value) {m_value = value;}
static Factory<SimpleBaseClass,SimpleClass1> myFactory;
private:
int m_value;
};
Factory<SimpleBaseClass,SimpleClass1> SimpleClass1::myFactory;
class SimpleClass2 : public SimpleBaseClass
{
public:
int getValue() {return m_value*100;}
void setValue(int value) {m_value = value;}
static Factory<SimpleBaseClass,SimpleClass2> myFactory;
private:
int m_value;
};
Factory<SimpleBaseClass,SimpleClass2> SimpleClass2::myFactory;
Instead
of a function pointer, the class has a static factory instance. Notice
that, since it is static, that we need to define it outside the class
as well. Otherwise we will have unresolved externals. To simplify the
use of types, we also added a type definition to our base class:
class SimpleBaseClass
{
public:
virtual int getValue() = 0;
virtual void setValue(int value) = 0;
typedef FactoryPlant<SimpleBaseClass> SimpleBaseClassFactory;
};
Our method that needs to create instances of each of these classes, based on the magic number, now looks like this:
SimpleBaseClass *someMethod (
std::map<int,SimpleBaseClass::SimpleBaseClassFactory *> factories,
int type)
{
return factories[type]->createInstance();
}
It is passed a map of factories (all deriving from
SimpleBaseClass::SimpleBaseClassFactory
, which is actually
FactoryPlant<SimpleBaseClass>
),
and it calls the createInstance of the factory (depending on the given
magic number). The application can now call this like this:
SimpleBaseClass *simpleInstance = NULL;
std::map<int,SimpleBaseClass::SimpleBaseClassFactory *> factories;
factories[1] = &SimpleClass1::myFactory;
factories[2] = &SimpleClass2::myFactory;
simpleInstance = someMethod (factories,1);
In
this example the build-up of the factories map is kept quite simple. In
more complex situations part of the map could be created by a shared
module. The application that simply needs to add his own factories (it
wants to support) to the map.
Conclusion
There are many different methods of writing
factories. I presented 6 of them here, each with their own advantages
and disadvantages. In my situation (the reason I wrote this article), I
actually needed the last one. I could use the function-pointer approach
but using C-style-function pointers did not look like good C++ to me. I
wasn't really fond of the clone-alternative either, so I started
experimenting with the template approach. I hope this article might
give you some ideas, and if you have some more ideas on writing
factories, just send me a mail.