Picture Inheritance Problems
Picture Inheritance Problems
Sometimes it’s easier to get a fuller understanding of programming concepts when seeing them in pictures. After all, a picture speaks a thousand words. Today I’ll illustrate the problems with inheritance outlined here – why deriving from frequently changing classes is not a good idea, and why we should avoid overriding concrete methods.
Here is an image of the inheritance hierarchy we’ll be using:
Class A is the super-parent root. It has a virtual method X(). A.X() has concrete behaviour. Since A.X() is virtual, child classes may choose to override the behaviour passed to them from their parent class.
In the figure, the same colour between classes indicates the same behaviour for X().
Classes G and J override the code of A.X(). The override behaviour of G.X() propagates to Classes K & L (same colour as G).
Note: For the sake of simplicity, I’ve left out situations where child classes only partially override the parent class behaviour of X(), e.g. base.X(). No doubt, having partial overrides won’t be an improvement.
Don’t derive from frequently changing concrete classes.
Base classes should be stable. Unfortunately, Class A is volatile – method A.X() is modified. The cascading effect on our inheritance structure is extensive:
A.X() has changed from yellow to green. As we can see from the figure, the side effects are significant. All subclasses of Class A, apart from Classes G, J, K and L, have X() change their behaviour. Let’s hope that was the desired outcome!
Furthermore, maybe this was meant to be a change that was to apply to all derived classes. Well, not all classes got the message. Overrides of X() for Classes G and J stopped four classes (G, J, K & L) getting the new behaviour. Overrides block the propagation of superclass behaviour.
Don’t override concrete methods.
A new requirement demands how X() functions for Class C and all its subclasses. Seems simple, right? We’ll override C.X().
Unfortunately, the override of G.X() is stopping the propagation of the new behaviour in C.X(). Sometimes that is what we want – the functionality from G.X() onwards should be “stronger” than what is flowing down from the parents. However, in this case, we don’t want that – we want the behaviour of C.X() to affect all of Class C’s children.
Manipulating behaviour correctly in deep inheritance hierarchies is tough. And people don’t plan on creating deep hierarchies – it’s something that happens over time. Inheritance is useful at times. It’s worth remembering that inheritance is a very rigid and strong relationship between classes. When using inheritance, we will prevent future problems if we derive from stable parents and if we avoid overriding concrete methods.
Leave a Reply
Want to join the discussion?Feel free to contribute!