C++ Class inheritance is bad

Interface inheritance is good.



Inheritance
Inheritance

I always have bright ideas in the morning. This morning I thought of ATL. And its usage of templates and inheritance. I figured it is a typical inheritance for implementation case. Which is bad.

Inheritance of interfaces is an implementation of the ‘behavior extension’ concept. Which is good. Interface inheritance is used to re-use the behaviour of the base interface. Just the behaviour. The interface is completely decoupled from the class which implements it. Very good indeed. And surprisingly absent from some key OO designs and implementations in widespread use today.

W3C DOM objects are the typical example of bad inheritance. In DOM every object has properties and methods of an invisible root DOM object. So we have dozens or more, methods and properties, on each and every DOM object. Regardless of its required behaviour. Same on each and every DOM object. This is in many cases, much, much more than we need on a particular object. Example :

DOM element “XML” is represented with a DOM object which has all the visual properties and methods as (for example) DIV element has. And they are mostly totally unusable and are seriously getting in the way, especially when you are new to the DOM scripting/programming, and its zillions of objects, methods, events and properties. A sure recipe for making many mistakes and introducing bugs.

Another example of evil inheritance is MFC. A naive and elaborate hierarchy of classes (not interfaces), which makes for an sea of very heavy classes with hundreds of methods and those crazy “Hungarian notation” named variables. While working in your favorite IDE they are ALL there even if you do not need them and do not have a clue what are they all for. Again, a sure sign you are bound to make bugs. If you use MFC, that is.

Information Hiding

This is the KEY problem with class inheritance: information overflow. A total opposite of the first law of OO: Information must be hidden as much as possible. So-called ‘information hiding’. A foundation corner stone of OO. Sadly broken by the likes of MFC, DOM ….. and somewhat by ATL.

Yes, I like ATL because it uses C++ to the full and shows (same as ISO C++ std lib) very clearly, why C++ is very elegant and complete. Of course, if you know how to make it be that way.

Sadly, the ATL team, I suppose under the dark and large shadow of MFC, adopted class inheritance approach, too. Although much better variety than MFC. But (sadly) ATL classes, at the end of the day, inherit from template instances which are nothing else but classes, again. Only the code looks more snazzy.

ATL implementation inheritance pattern is this:

Ok, some of you might have recognized this as a so-called “Curiously Recurring Template Pattern”. But this is basically  “inheritance for implementation” idiom. Looks pretty snazzy, does it not?

Well, the first problem (with any kind of inheritance) shows particularly in modern IDE’s. Imagine now that Base has many methods. You want to use only one. But, in the IDE, all the others are immediately and readily available and very close. Too close for comfort. Your IDE and it’s VS editor IntelliSense will show ALL of them. Many of them. But you need only one. Found it yet? And this is just for a single one class inherited.

To make matter worse, in MFC and ATL you inherit several classes. And each one has many methods and properties. And they are all available to you at the same time. Providing all the methods visible from al o them all the time; through the IntelliSense feature. A lot of them. And still, you want only one. All of this is directly opposite of key postulate of the OO: information hiding. Much better and dare I say proper way is this:

This concept is called Delegation. We delegate the job to the instance of the B. Delegation gives total encapsulation of X and its implementation. Template instance B<X> is now totally hidden. Information hiding is achieved: Users of X do not know what B<X> does internally. They just use it. They are even unaware of its existence. This also means that replacing B<X> with e.g. C<X> is completely ok and painless. Provided they both have the same interface. This does not break existing X usage in any way. Clients of X are completely isolated from this change. Of course, both B<X> and C<X> have to have a clean and simple and narrow interface. As every other class has to.

Narrow interface? Ok, this is another key concept. I shall stop here, this is all that I thought off, this morning …

DBJ

Leave a Reply