Introduction
This article gives an introduction to the concept I call dynamic inheritance. In dynamic inheritance, you can override the implementation of an interface function at runtime. This is useful when you have different possible implementations of interface functions and you need to use different combinations in different scenarios.
I have used C++ code to explain the concept here. But I have included both C++ and Java sample codes along with this article. But the concept remains the same in both languages.
Motivation
Recently I had a strange requirement to implement in my system. I had to implement a set of interface functions. There are many possible implementations for each of the interface functions. Only at runtime, I can decide what will be the implementations for each of the interface functions in the object. Say for example, I have an interface with four functions, say:
virtual void DynamicFunction1() =0;
virtual void DynamicFunction2() =0;
virtual void DynamicFunction3() =0;
virtual void DynamicFunction4() =0;
In different scenarios, I need to give different implementations for one/more of the interface functions. If I go for normal inheritance, I need to give implementations for all the possible combinations making the code difficult to maintain. Also code duplication was too high. This problem made me think of dynamic inheritance. Can I create a dynamic inheritance assembling the required function implementations together at runtime?
This is what I came up with.
Here we create a composite object with the objects with high priority functions at the outermost layer and less priority functions at the inner layers. The advantage of this implementation is that we don't need to create numerous classes with all possible combinations of function implementation.
Imagine there are 10 different possible implementations for DynamicFunction1()
and 3 different possible implementations for DynamicFunction2()
and 4 different possible implementations for DynamicFunction3()
and one implementation for DynamicFunction4()
.
If we use inheritance, we need to define 10x3x4x1 different classes to give all possible requirements. This makes the code really complex and difficult to maintain.
How can we use dynamic inheritance in these scenarios? I will explain this with an example. Imagine we have three different classes with different implementations of interface functions as shown below.
Class1
– Has implementation of DynamicFunction1()
Class2
– Has implementation of DynamicFunction1()
and DynamicFunction2()
Class3
– Has implementation of DynamicFunction3()
With dynamic inheritance, we create a composite object with the separate objects wrapped together according to their priority. For the outside world, they can call any of the interface functions on the composite object. The call will be routed as shown below. (You can create different variants of this. Here I am implementing dynamic inheritance.) Any function call, if there is an implementation in that layer, that function will be executed and returned. Now if there is no implementation, then the call will be routed to the inner layer. If none of the layers provide the implementation, default implementation in the core will be executed.
Now we will take a close look at a couple of cases in our example. Imagine there is a call to DynamicFunction1()
on composite object. Since there is an implementation for the function DynamicFunction1()
in the outermost layer, it will execute that and return.
Imagine, there is a call to DynamicFunction2()
on composite object. Since the outermost layer doesn't have an implementation for DynamicFunction2()
, the call will be routed to the next inner layer.
Sample Code
Define the Interface
Class CDynamicInterface
{
protected :
CDynamicInterface * pcChild;
public :
virtual ~CDynamicInterface(){};
virtual void DynamicFunction1() =0;
virtual void DynamicFunction2() =0;
virtual void DynamicFunction3() =0;
virtual void DynamicFunction4() =0;
};
Core Provides the Default Implementation for All the Functions
class CDynamicFunctionCore : public CDynamicInterface
{
public:
CDynamicFunctionCore();
CDynamicFunctionCore(CDynamicInterface * pcChildPtr);
virtual ~CDynamicFunctionCore();
virtual void DynamicFunction1();
virtual void DynamicFunction2();
virtual void DynamicFunction3();
virtual void DynamicFunction4();
};
Core Constructor
CDynamicFunctionCore::CDynamicFunctionCore(CDynamicInterface * pcChildPtr)
{
cout<<"Constructor CDynamicFunctionCore\n";
pcChild = pcChildPtr;
}
CDynamicFunctionCore::CDynamicFunctionCore()
{
cout<<"Constructor CDynamicFunctionCore\n";
pcChild = NULL;
}
Give Default Implementation for All Core Functions
void CDynamicFunctionCore::DynamicFunction1()
{
if(pcChild)
pcChild->DynamicFunction1();
else
cout<<"Define DynamicFunction1\n";
}
Define new classes with different interface function definitions. Keep in mind that you should always derive class from CDynamicFunctionCore
class so that you have the default implementations of all functions.
class CDynamicFunction1A : public CDynamicFunctionCore
{
public:
CDynamicFunction1A(CDynamicInterface * pcChildPtr);
CDynamicFunction1A(){};
virtual ~CDynamicFunction1A();
virtual void DynamicFunction1();
};
Initialization and Operations on Composite Object
CDynamicInterface * MyComposite = new CDynamicFunction1A(new CDynamicFunction1B2B());
MyComposite->DynamicFunction1();
MyComposite->DynamicFunction2();
MyComposite->DynamicFunction3();
MyComposite->DynamicFunction4();
delete MyComposite;
Object Destruction – Make All Destructors Virtual
CDynamicFunctionCore::~CDynamicFunctionCore()
{
cout<<"Destructor CDynamicFunctionCore\n";
if(pcChild)
delete pcChild;
pcChild = NULL;
}
But there is a catch. Imagine the function DynamicFunction2()
in CDynamicFunction1B2B()
calls DynamicFunction1()
, which will be the function executed? Of course, the object of type CDynamicFunction1B2B
doesn't have any information about its outer layer and it won't call the outermost DynamicFunction1()
. Instead it will call it on DynamicFunction1()
. What can be done if we need to call the DynamicFunction1()
of the composite from within a function? We need to keep a reference to the parent just like the reference to the child. Can we do it along with the object creation? It is not possible to do along with the object creation if we initialize as given below:
CDynamicInterface * MyComposite = new CDynamicFunction1A(new CDynamicFunction1B2B());
Reason for this is, the object MyComposite
creation is not complete when we call the constructor of CDynamicFunction1B2B()
. That means we can't pass reference to MyComposite
in constructor. What we can do is, create a new function which will set the parent pointer immediately after construction of the MyComposite
. Refer to the sample code given along with this article to see the complete implementation. Now if you need to call the outermost (parent) function, use the parent pointer to call the function.
Class Diagram
Summary
Dynamic inheritance helps you to override functions at runtime. This is useful when there are lots of different implementations possible for each of the interface functions. This avoids creation of large number of class definitions with all the possible combination of interface functions. This increases the code reuse and maintainability of the code. This becomes handier when we can define classes in external libraries (in DLLs or SOs) and at runtime, wrap objects together to create composites with new behaviors.
History
- 17th October, 2006: Initial post