Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C++

A C++ Object Factory

Rate me:
Please Sign up or sign in to vote.
4.91/5 (8 votes)
25 Mar 2013CPOL5 min read 57.8K   14   12
A C++ object factory.

The Problem

In object oriented programming, it is very common to have a scenario where a large number of classes share the same base class and the particular implementation needs to be created at runtime based on some specific parameters, for example a class name held in a string.

Standard C++ does not provide the same type of reflection mechanism that other languages use to achieve this, such as C# :

C++
System.Reflection.Assembly.GetExecutingAssembly()
      .CreateInstance(string className)

or in Java:

C++
Class.forName(className).getConstructor(String.class).newInstance(arg);

However, C++ does not allow us to do such things; we have to come up with another solution. The basic pattern, or set of patterns, that help us achieve this are factory patterns.

A Simple Solution

The Base Class

Our base class is defined as an abstract class as follows:

C++
#ifndef CPPFACTORY_MYBASECLASS_H
#define CPPFACTORY_MYBASECLASS_H

class MyBaseClass
{
public:
    virtual void doSomething() = 0;
};

#endif // CPPFACTORY_MYBASECLASS_H

The Factory Class

A factory method can then be defined as a static method that can be used to create instances of MyBaseClass. We could define this as a static method on MyBaseClass itself, although it is generally good practice in object oriented development that a class serves a single purpose. Therefore, lets create a factory class:

C++
#ifndef CPPFACTORY_MYFACTORY_H
#define CPPFACTORY_MYFACTORY_H

#include "MyBaseClass.h"
#include <memory>
#include <string>

using namespace std;

class MyFactory
{
public:
    static shared_ptr<MyBaseClass> CreateInstance(string name);
};

#endif // CPPFACTORY_MYFACTORY_H

The factory method is expected to create an instance of a class named name that is derived from MyBaseClass and return it as a shared pointer, as it will relinquish ownership of the object to the caller.

We shall return to the implementation of the method shortly.

Some Derived Classes

So lets implement a couple of derived classes:

C++
#ifndef CPPFACTORY_DERIVEDCLASSONE_H
#define CPPFACTORY_DERIVEDCLASSONE_H

#include "MyBaseClass.h"
#include <iostream>
using namespace std;

class DerivedClassOne : public MyBaseClass
{
public:
    DerivedClassOne(){};
    virtual ~DerivedClassOne(){};

    virtual void doSomething() { cout << "I am class one" << endl; }
};

#endif // CPPFACTORY_DERIVEDCLASSONE_H

and

C++
#ifndef CPPFACTORY_DERIVEDCLASSTWO_H
#define CPPFACTORY_DERIVEDCLASSTWO_H

#include "MyBaseClass.h"
#include <iostream>
using namespace std;

class DerivedClassTwo : public MyBaseClass
{
public:
    DerivedClassTwo(){};
    virtual ~DerivedClassTwo(){};

    virtual void doSomething() { cout << "I am class two" << endl; }
};

#endif // CPPFACTORY_DERIVEDCLASSTWO_H

A First Attempt at the Factory Method

A simple solution to the implementation of the factory method would be something like this:

C++
#include "MyFactorySimple.h"

#include "DerivedClassOne.h"
#include "DerivedClassTwo.h"

shared_ptr<MyBaseClass> MyFactory::CreateInstance(string name)
{
    MyBaseClass * instance = nullptr;

    if(name == "one")
        instance = new DerivedClassOne();

    if(name == "two")
        instance = new DerivedClassTwo();

    if(instance != nullptr)
        return std::shared_ptr<MyBaseClass>(instance);
    else
        return nullptr;
}

The factory determines which concrete class to create and has knowledge of every class via the class headers.

Running the application

A simple main function is now needed so that we can test our implementation:

C++
#include "MyFactorySimple.h"

int main(int argc, char** argv)
{
    auto instanceOne = MyFactory::CreateInstance("one");
    auto instanceTwo = MyFactory::CreateInstance("two");

    instanceOne->doSomething();
    instanceTwo->doSomething();

    return 0;
}

A Visual Studio Project (SimpleFactory.vcxproj) is included with the source code accompanying this article which can be built and run giving the following output:

C++
I am class one
I am class two

Problems with the Simple Solution

On the surface this looks like a good solution and it possibly is in some cases. However, what happens if we have a lot of classes deriving from MyBaseClass? We keep having to add the includes and the compare – construct code.

The problem now is that the factory has an explicit dependency on all the derived classes, which is not ideal. We need to come up with a better solution; one that removes the need for constantly adding to the MyFactory::Create. This is where the idea of a registry of factory methods can help us.

A Revised Factory Class

One of our main objectives is to remove the dependencies on the derived classes from the factory. However, we still need to allow the factory to trigger the creation of instances. One way to do this is for the main factory class to maintain a registry of factory functions that can be defined elsewhere. When the factory class needs to create an instance of a derived class, it can look up the factory function in this registry. The registry is defined as follows:

C++
map<string, function<MyBaseClass*(void)>> factoryFunctionRegistry;

It is a map, keyed on a string with values as functions that return a pointer to an instance of a class based on MyBaseClass.

We can then have a method on MyFactory which can add a factory function to the registry:

C++
void MyFactory::RegisterFactoryFunction(string name,
function<MyBaseClass*(void)> classFactoryFunction)
{
    // register the class factory function
    factoryFunctionRegistry[name] = classFactoryFunction;
}

The Create method can then be changed as follows:

C++
shared_ptr<MyBaseClass> MyFactory::Create(string name)
{
    MyBaseClass * instance = nullptr;

    // find name in the registry and call factory method.
    auto it = factoryFunctionRegistry.find(name);
    if(it != factoryFunctionRegistry.end())
        instance = it->second();

    // wrap instance in a shared ptr and return
    if(instance != nullptr)
        return std::shared_ptr<MyBaseClass>(instance);
    else
        return nullptr;
}

So how do we go about registering the classes in a way that keeps dependencies to a minimum? We cannot easily have instances of the derived classes register themselves as we can’t create instances without the class being registered. The fact that we need the class registered, not the object gives us a hint that we may need some static variables or members to do this.

I stress that the way I am going to do this may not be the best in all scenarios. I am deeply suspicious of static variables and members, as static initialization can be a minefield. However, I will press on, as the solution serves the purpose of this example and it is up to the reader to determine whether a solution they use needs to follow different rules and design.

Firstly we define a method on MyFactory to obtain the singleton instance:

C++
MyFactory * MyFactory::Instance()
{
    static MyFactory factory;
    return &factory;
}

We cannot call the following from the global context:

C++
MyFactory::Instance()->RegisterFactoryFunction(name, classFactoryFunction);

I have therefore created a Registrar class that will do the call for us in its constructor:

C++
class Registrar {
public:
    Registrar(string className, function<MyBaseClass*(void)> classFactoryFunction);
};
...
Registrar::Registrar(string name, function<MyBaseClass*(void)> classFactoryFunction)
{
    // register the class factory function 
    MyFactory::Instance()->RegisterFactoryFunction(name, classFactoryFunction);
}

Once we have this, we can create static instances of this in the source files of the derived classes as follows (DerivedClassOne):

C++
static Registrar registrar("one",
[](void) -> MyBaseClass * { return new DervedClassOne();});

As it turns out, this code can be duplicated in all derived classes so a quick pre processor define as follows:

C++
#define REGISTER_CLASS(NAME, TYPE) \
    static Registrar registrar(NAME, \
        [](void) -> MyBaseClass * { return new TYPE();});

This uses the new C++ lambda support to declare anonymous functions. We then only need add the following to each derived class source file:

C++
REGISTER_CLASS("one", DerivedClassOne);

Update 25th January 2013

We Can Do Better …

Although the #define solution provides a neat implementation we could probably do this in a bit more of a C++ style by converting the Registrar class into a template class as follows:

C++
template<class T>
class Registrar {
public:
    Registrar(string className)
    {
        // register the class factory function 
        MyFactory::Instance()->RegisterFactoryFunction(name,
                [](void) -> MyBaseClass * { return new T();});
    }
};

And now we can replace the use of the macro by

C++
static Registrar<DerivedClassOne> registrar("one");

We now have a function registry based factory class defined and the main function can now be slightly modified as follows:

C++
#include "MyFactory.h"

int main(int argc, char** argv)
{
    auto instanceOne = MyFactory::Instance()->Create("one");
    auto instanceTwo = MyFactory::Instance()->Create("two");

    instanceOne->doSomething();
    instanceTwo->doSomething();

    return 0;
}

We can now build and run the project and get the following output:

I am class one
I am class two

References:

This article was originally posted at http://blog.jsolutions.co.uk?p=545

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer jSolutions Ltd
United Kingdom United Kingdom
John Cumming has been working in software engineering for over 15 years. With qualifications in a mechanical engineering discipline and initial employment in chemical engineering research, he has engineering experience and knowledge to bring to a variety of software projects.

John has a wide range of experience in a variety of technologies, including web applications, distributed architectures and desktop applications and has applied his knowledge and experience in a variety of roles in R&D projects, Integration Consultancy and Enterprise Architecture.

John is experienced in Agile development processes such as XP and Scrum and is a Certified Scrum Professional.

* C++ - UNIX, Windows, COM, MFC, ATL, CLI, CORBA, Qt, boost
* C# - .NET 4.5, WPF, Prism, XAML
* XML - XML, XSLT, XSD, SOAP, XSL-FO
* Security – Cryptography, Key Management, Smart Cards
* Agile and Scrum

LinkedIn
Facebook
jSolutions
This is a Organisation (No members)


Comments and Discussions

 
QuestionThe full code Pin
Gernot Frisch28-Jan-19 21:50
Gernot Frisch28-Jan-19 21:50 
QuestionHow to handle non default constructors Pin
Member 1368195116-Feb-18 8:00
Member 1368195116-Feb-18 8:00 
QuestionQuestion Pin
Member 1343494728-Sep-17 1:44
Member 1343494728-Sep-17 1:44 
Questionnumbered, counted instances... Pin
LastBlow128-Jul-16 15:05
LastBlow128-Jul-16 15:05 
Questionvery Nice Article. Also need help in sourcecode Pin
Member 1181532415-Jul-16 21:11
Member 1181532415-Jul-16 21:11 
AnswerRe: very Nice Article. Also need help in sourcecode Pin
LastBlow117-Jul-16 20:19
LastBlow117-Jul-16 20:19 
QuestionVery nice! Pin
Member 1253543220-May-16 0:58
Member 1253543220-May-16 0:58 
Questionsource code missing Pin
Member 121234088-Nov-15 2:50
Member 121234088-Nov-15 2:50 
AnswerRe: source code missing Pin
jsolutions_uk22-Nov-15 21:00
jsolutions_uk22-Nov-15 21:00 
GeneralRe: source code missing Pin
C++myLife14-Mar-17 23:31
C++myLife14-Mar-17 23:31 
GeneralMy vote of 5 Pin
Bartlomiej Filipek25-Mar-13 7:23
Bartlomiej Filipek25-Mar-13 7:23 
GeneralRe: My vote of 5 Pin
jsolutions_uk25-Mar-13 8:30
jsolutions_uk25-Mar-13 8:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.