Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Different Ways of Implementing Factories

0.00/5 (No votes)
26 Mar 2003 1  
This article presents different ways of implementing factories, each with their own advantages and disadvantages.

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) 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 not 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 a 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<bt>
   {
   public:
      Factory() {}
      virtual ~Factory() {}
      virtual BT *createInstance() {return new ST;}
   };</bt>

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, 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 its 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, please leave a comment below.

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here