Some time ago I made a suggestion about implementing dynamic inheritance in Delphi. A little later I came up with an idea how to implement an oop design pattern that would allow making this in general oop languages like delphi without modifications. The following article will explain this idea.
Someone would ask why this kind of inheritance can be useful. I felt the necessity for something like this when found that implementing a plugin system with packages has some limitations. The main limitation is that no good inheritance is possible. Imagine if there's a base object in the program that corresponds to some interface. Plugin A lives in another package and is aware of such interface and inherits from this object. Plugin B also knows about the same interface and also can inherit the same way. The problem here is that it's impossible for them to inherit and live in the same program at the same time. Well, if the interface implemented like enumerator of classes then no problem. But if I just want to change a tiny bit of functionality of Plugin A (that is always possible with static inheritance), I could not.
The idea below is actually not new. This page explains a similar concept in c++. I came up with it independently, so there are other features or observations possible. Besides, my implementation details is in Delphi/Pascal so there are delphi-specific sides here. The project below is an example implementing plugin system based on packages and quotes here will be from the source files of this project.
So the idea here is to split the inheritance tree into several objects while providing a convenient OOP-like interface for conventional tasks like inheritance. We introduce a common ancestor of all our objects. The goal of this object is to keep reference to the next ancestor of the inheritance tree. The actual ancestor will be passed to this object in run-time.
TDynhBase = class private fInherited: TDynhBase; public constructor Create(Inh: TDynhBase);virtual; ... end;
And the full object inheritance tree will be created using nested constructions like
The same logic can be imlemented with iterators or other ways.
Let's introduce an object that can enumerate strings and serves as an interface for the following descendants.
TDynhStringList = class(TDynhBase) public constructor Create(Inh: TDynhBase);override; function GetCount: integer;virtual; function GetItem(Index: integer): string;virtual; end;
in general oop GetCount and GetItem can be safely introduced as abstract methods. But our system will require to implement the folllowing bodies.
function TDynhStringList.GetCount: integer; begin Result:=TDynhStringList(fInherited).GetCount; end; function TDynhStringList.GetItem(Index: integer): string; begin Result:=TDynhStringList(fInherited).GetItem(Index); end;
This strange actions actually is only ones specific to thie design pattern. Saying in other words, this is a trick that would allow dynamic inheritance without introducing new language constructions. You should do the same for any virtual methods you introduce. This is because this calls represent the inherited calls of generall oop.
So when everything strange is done, we can now safely inherit in our Plugin A for implementing our string provider
TPluginAStringList = class(TDynhStringList) public function GetCount: integer;override; function GetItem(Index: integer): string;override; end; ... function TPluginAStringList.GetCount: integer; begin Result:=(inherited GetCount) + MyStringCount; end;
In order to make things simpler, I quoted only one method implementation here. Let's look at it. When you see (inherited GetCount) calls in a conventional program, you can guess that this calls would call some previously mentioned overriden method in the objects hierarchy. But in this case it drops directly TDynhStringList.GetCount and only there. And if you remember the implementaion of this method looks like this
... Result:=TDynhStringList(fInherited).GetCount ...
and as you see it would call the GetCount method of dynamically provided piece of our inheritance tree. This is the magic behind this approach.
The actual project you can download implements to packages as plugins and the power of this system can be seen in the ini file it uses
[MAIN] Packages=DynhPluginA.bpl,DynhPluginB.bpl ListInheritance=TPluginBStringList,TPluginAStringList
Packages here are two packages used by the main program and ListInheritance is the actual order of object in our inheritance tree. I can safely exchange them without recompiling any piece of software (program or package) and the list in the listbox will change the order of items.
I mentioned the plugins as possible application for this system. But also there are plenty of others and an interesting thing about such inheritance that it can apply inheritance not only to code, but also to data. So when two instances call the single instance of the ancestor. In general oop if some object is a part of another, the former sometimes needs returning data for the host. It is usually done with callbacks or just by returning the data needed. But if you dynamically inherit with the pattern explained, you can implement owning by inheriting and this can bring some benefits.
You can download the project files here: DelphiDynInhSources.zip
Published 2012 may