Click here to Skip to main content
15,867,330 members
Articles / Programming Languages / Java

Dynamic Inheritance

Rate me:
Please Sign up or sign in to vote.
4.25/5 (17 votes)
17 Oct 2006CPOL5 min read 55.2K   399   17   6
A design idea - Dynamic Inheritance

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:

C++
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.

Sample Image - dynami1.gif

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.

Sample Image - dynami3.gif

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 Image - dynami5.gif

Sample Code

Define the Interface

C++
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

C++
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

C++
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

C++
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.

C++
class CDynamicFunction1A : public CDynamicFunctionCore  
{
public:
    CDynamicFunction1A(CDynamicInterface * pcChildPtr);
    CDynamicFunction1A(){};
    virtual ~CDynamicFunction1A();
    virtual void DynamicFunction1();
};

Initialization and Operations on Composite Object

C++
CDynamicInterface * MyComposite = new CDynamicFunction1A(new CDynamicFunction1B2B());

MyComposite->DynamicFunction1();
MyComposite->DynamicFunction2();
MyComposite->DynamicFunction3();
MyComposite->DynamicFunction4();

delete MyComposite;

Object Destruction – Make All Destructors Virtual

C++
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:

C++
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

Sample Image - dynami15.gif

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

License

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


Written By
Software Developer (Senior) DWS
Australia Australia

Comments and Discussions

 
QuestionNice pattern that needs wider usage pattern Pin
maxerist4-May-12 2:40
maxerist4-May-12 2:40 
GeneralPatterns... Pin
Qanuc2-Nov-06 21:05
Qanuc2-Nov-06 21:05 
GeneralMaybe better approach Pin
mYsZa23-Oct-06 21:56
mYsZa23-Oct-06 21:56 
GeneralRe: Maybe better approach Pin
Boby Thomas P23-Oct-06 23:29
Boby Thomas P23-Oct-06 23:29 
GeneralRe: Maybe better approach Pin
Martin Lercher23-Oct-06 23:45
Martin Lercher23-Oct-06 23:45 
That's a technical implementation detail. If you use the strategy pattern (or lightweight with delegates or function) which adresses the "concept of dynamic inheritance" the *compiler* keeps track of all the subtleties. Not the source code. Another advantage is that this pattern is well understood by many people so any skilled coder is able to provide maintenance.

Martin
AnswerRe: Maybe better approach Pin
mYsZa24-Oct-06 0:39
mYsZa24-Oct-06 0:39 

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.