Saturday, October 15, 2011

Prototype Pattern in C++, Dynamic instantiation.

Before starting the discussion of Prototype pattern, We will understand the problem of Dynamic instantiation.

Given a typename ( a class name ) in C++ as a string, how an object can be created of that type?

First off, there is no language level support for dynamic instantiation in C++ unlike Java and C#. Since in C++, there is no common base class for all classes, dynamic instantiation support is generally not possible. MFC, a C++ framework provides support for Dynamic instantiation as all classed are derived from CObject. Let us try in the same line, making use of common base and Prototype pattern to achieve Dynamic instantiation.

Prototype pattern imposes a class to have a method, Clone, whose sole purpose is to generate a new object of same type. With a clone-able object, we could be able to generate a new object of same type. In order to achieve this, all classes must be derived from a common base class. And in that common base class, we can have a pure virtual member function(interface), Clone. So, all classes derived from that, would be clone-able.

Let us call that common base class "Object" as in Java. My minimalistic implementation of the Object is as given here,

class Object
{
public:
virtual ~Object() {}
virtual Object* Clone() const = 0;
static Object *MakeObject(std::string type);
static void AddNewClassInfo(std::string type, Object* in);
static std::map objectsTable;
};

Clone is the key method of this abstract class. Implementation of the same could be like the one given below.

class Derived : public Object
{
Object *Clone() { new Derived(); }
};

So, all the classes derived from Object can be cloned using this method call. In order to "Clone" an object, we should have one initial object, prototypical instance. And we should be able to get that base prototypical instance from the type name. If we could be able to do this, then Making an object using type-name alone is a cake-walk. "objectsTable" map in Object class holds the aforesaid mapping; a map of type-name and a prototypical instance.

Adding a prototypical instance can be done through the method, "AddNewClassInfo", whose implementation can be like this.

// Populate map with type-name and its corresponding prototype.
void Object::AddNewClassInfo(std::string type, Object *in)
{
objectsTable[type] = in;
}

Now the implementation of "MakeObject" must be easily understandable.

Object* Object::MakeObject(std::string type)
{
std::map::iterator itr;
itr = objectsTable.find(type);
if( itr != objectsTable.end())
{
return itr->second->Clone(); // Clone the prototypical instance.
}
else
return NULL;
}

All the essential implementations of "Object" is done. Since it has a static member, it should be defined in CPP file as given here,

std::map Object::objectsTable;

OK. Now let us create a derived class from Object and test this implementation.

#include "Object.h"

class DynamicInstantiable : public Object
{
public:
DynamicInstantiable() {}
void SayHello()
{
std::cout << "Hello! " << std::endl;
}

Object* Clone() const
{
return new DynamicInstantiable();
}
};

The above class has the implementation for Clone method and is derived from Object. Now, this type must be added in Static Map in "Object" class using Object::AddNewClassInfo. We have several options here. But I would like to keep things simple. So, I have added a new header file which has a global function "InitializeDynamicObjects". And this method must be called in "main()" function in the very first line itself.

A sample implementation is given below.

void InitializeDynamicObjects()
{
Object::AddNewClassInfo("DynamicInstantiable", new DynamicInstantiable());
}

Here is the sample Main function, I have written to test the code.

#include "DynamicInstantiable.h"
#include "ObjectInitializer.h"
using namespace std;

int main()
{
InitializeDynamicObjects();

DynamicInstantiable* newInst = dynamic_cast(Object::MakeObject("DynamicInstantiable"));

if( newInst != NULL)
{
newInst->SayHello();
}

cout << "Hello World" << endl;
}


I hope this post explains Prototype clearly. Comments and Queries are welcome.

Thanks for Reading.

References:

1 comment: