OK, an unnamed but reliable source and I were have a discussion over lunch the other day. I contended that the importance of inheritance to OO has traditionally been hugely overstated. I went so far as to even suggest that for almost any problem to which a solution could be crafted using inheritance, I could craft an equally simple and effective solution using just composition.
I may have exaggerated that last point, but I do believe too many young programmers are fooled into thinking that if they are not using inheritance in some way, then they are not actually doing OO. I have seen so many designs that have inheritance shoe horned in for no good reason that sometimes I want to vomit. Relenting a little, especially after cuddling up with GoF since that discussion, I will admit that there are more problems than I originally credited where inheritance, or more specifically polymorphism, does seem to have a legitimate role. (Bear in mind that when it comes to polymorphism, I think interfaces can often achieve the desired goal more safely.)
I would still argue that there should be a natural hesitation around using inheritance. My favorite object lesson is in Josh Bloch’s Effective Java, where he uses the example of extending a Point class, that just has coordinates, with a ColorPoint class that adds a Color property. The problem arises when you try to show the commutation of equivalence between a Point instance and a ColorPoint instance. As an exercise for the reader, write both classes, then a test class that instantiates both and tests point.equals(colorPoint) and colorPoint.equals(point). You do write equals for all your classes, right?
This is not a contrived example, by any means. I have seen any number of programmers add attributes to descendants without thinking about it. Heck, I know I have done it myself, more than once. But this example should give you pause. It should make you think through some of the important constraints of inheritance and polymorphism, like commutation of equivalence and Liskov’s substitution principle. Let me put that another way, if using a nominally polymorphic design, you find yourself using the instanceof operator, you have failed.
OK, those are all some pretty bold statements, but here’s my challenge. Send me a non-contrived code example that proves me wrong in a non-fringe case. Go on, I dare you. For my sanity, please restrict your examples to Java. (Sorry, my C++ just ain’t up to the challenge, and I don’t want to have to deal with the additional design and style problems C++ typically encourages.) I’ll warn you in advance that I am accomplished at re-factoring, and if I can re-factor your code into a clearer, simpler form to debunk your example, I will. Unlike Wil Shipley, who recently posited a code challenge, I won’t nitpick over style (too much), but stick to the core notion of the challenge, which is about inheritance and design goals.
I have left the comments and trackback open. Comments are moderated, but I am pretty responsive. You can also send me your code examples at firstname.lastname@example.org. I may also issue this challenge in my next podcast.