#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 SimpleClassFactoryThis 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.
{
public:
SimpleClass *createInstance() {return new SimpleClass;}
};
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 SimpleBaseClassA multi-factory implementation might look like this:
{
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;
};
class SimpleClassFactoryThis 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.
{
public:
SimpleBaseClass *createInstance1() {return new SimpleClass1;}
SimpleBaseClass *createInstance2() {return new SimpleClass2;}
};
Factory 3: the dispatching multi-factory
This approach is very similar to the previous one, but uses a dispatching method.class SimpleClassFactoryIf one needs to create an instance of a certain type (1 or 2), it can create the instance like this:
{
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;
}
SimpleBaseClass *simpleInstance = NULL;I admit that this is only a small difference with approach 2, but it can be useful in some situations.
SimpleClassFactory simpleClassFactory;
simpleInstance = simpleClassFactory.createInstance(1);
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).
class SimpleClass1 : public SimpleBaseClassEach class is given a static '
{
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;
};
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 (The application can feed it with the supported factories like this:
std::map<int,FactoryFunction> factoryFunctions, int type)
{
return factoryFunctions[type]();
}
std::map<int,FactoryFunction> factoryFunctions;The method is then called like this:
factoryFunctions[1] = &SimpleClass1::createInstance;
factoryFunctions[2] = &SimpleClass2::createInstance;
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 SimpleBaseClassThe method that needs to create the instances then needs a map of these 'dummy' instances instead of function pointers:
{
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;
};
SimpleBaseClass *someMethod (And using it works like this:
std::map<int,SimpleBaseClass *> clonables, int type)
{
return clonables[type]->clone();
}
SimpleBaseClass *simpleInstance = NULL;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.
SimpleClass1 clonable1;
SimpleClass2 clonable2;
std::map<int,SimpleBaseClass *> clonables;
clonables[1] = &clonable1;
clonables[2] = &clonable2;
simpleInstance = someMethod (clonables,1);
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>The
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;}
};
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 SimpleBaseClassInstead 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:
{
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;
class SimpleBaseClassOur method that needs to create instances of each of these classes, based on the magic number, now looks like this:
{
public:
virtual int getValue() = 0;
virtual void setValue(int value) = 0;
typedef FactoryPlant<SimpleBaseClass> SimpleBaseClassFactory;
};
SimpleBaseClass *someMethod (It is passed a map of factories (all deriving from
std::map<int,SimpleBaseClass::SimpleBaseClassFactory *> factories,
int type)
{
return factories[type]->createInstance();
}
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;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.
std::map<int,SimpleBaseClass::SimpleBaseClassFactory *> factories;
factories[1] = &SimpleClass1::myFactory;
factories[2] = &SimpleClass2::myFactory;
simpleInstance = someMethod (factories,1);