Saturday, 5 April 2008

The case against inheritance

I was posting a reply to a codeproject lounge comment the other day when I got a response that challenged me to justify why its good multiple inheritance is not in c#.

I'll cut and copy my response here

You don't have to know what every method does of the parent class(es). You just have to implement the interface.

If you don't know what the parent class(es) do then when you modify an existing class structure you may leave in (fail to override) code that will just not work. And that applies all the way up the inheritance tree too. The deeper the inheritance tree the more you have to know if your going to modify the structure. Usually I find just using a class is fine, but modifying a class hierarchy to me requires you understand it on a deeper level.

Now maybe this is just an argument against inheritance in general but multiple inheritance just makes it harder and binds things together more tightly.

In general I'm moving away from inheritance as it has
1. largely failed to provide code reuse on a big scale,
2. has a habit of hiding implementation details and
3. locks in structure based on assumptions/goals that change and make it hard(er) to modify.

The best form of code reuse I have heard of is in components, in particular VB components and they don't use inheritance in that model as far as I know. Though internally the component may.

One of my experiences with the problems of inheritance hierarchies was when designing a bunch of firewire camera classes to grab frames from multiple cameras and save them to disk, in c++. We initially designed a nice little class hierarchy based on a a Basler BCAM library with a common frame grabbing thread. The aim being that if we wanted to use a different library we could just replace the Basler camera library with the new camera library object.

Of course along came a second CMU library and we found that this was trickier than expected. The libraries grabbed frames in a different manner to each other and this is where we went wrong. The first one had a sequence of 1. Set callback in camera library, 2. Start 3. Wait for Frames to arrive. 4. End grabbing frames. The second library had a sequence of 1. Begin Grabbing Frames, 2. Grab Frame, 3. End grabbing frames.

So we tried to fit the second camera library to the existing structure, bad move. Eventually it got done but it was a huge effort just to maintain the same frame grabbing thread code.

It would have been much easier to have had an interface to each library and write code for each type of camera. What really happened is I foolishly believed that the original hierarchy we had defined would apply to all camera libraries and that a new camera would simply slide into our class hierarchy. This wasn't true, we had bound our existing classes too tightly to the Basler library. At that point we should have abandoned modifying the existing class hierarchy and moved to a different code base for the new type of camera library.

The real point about this is that because I had a class hierarchy I thought I should reuse it. If it had been just an interface then I probably would have been less emotionally and politically tied into it. What can I say, I was young and inexperienced, none the less it is a lesson well learned.

The lessons I learnt

  • Changing a class hierarchy may impact on many areas.

  • Class hierarchies can be hard to change

  • Because they are hard to change they can tie you politically and emotionally into a way of thinking

  • Inherited code can be harder to reuse than helper classes.

  • Inheritance doesn't necessarily lead to much code reuse.

If you want to know the arguments a little further get the book Effective C# by Bill Wagner page 118

No comments: