Shyam has posted 8 posts at DZone. You can read more from them at their website. View Full User Profile

Is Inheritance Overrated? Needed even?

08.19.2009
| 10318 views |
  • submit to reddit

To give some context to this topic, the idea was brought forward to me by Alex Eagle. I was happily coding away when Alex sprung his idea for Composition over Inheritance for Noop – a language we are developing with testability and dependency injection in mind. My gut reaction was that this was blasphemy, and it couldn’t be done. You can’t just do away with inheritance, its one of the building blocks of OO based programming languages. But now, after I have let the idea digest for a few days, it doesn’t seem so far fetched any more. And here’s why.

Let me first talk about the biggest problems with vanilla inheritance as we have it in Java. Joshua Bloch hits it on the nail in his Effective Java book item about “Favoring composition over inheritance.” But lets do a quick recap anyway.

The biggest problem is that inheritance often ends up breaking encapsulation. This is because the child class depends on the implementation of the parent class. But between releases, something in the parent class implementation can change and can break all child classes without even touching its code. Another common gotcha is in how protected fields and members are used. Often, the parent class changes the value of fields depending on how methods are called. Not understanding this behavior often leads to buggy or simply wrong behavior from the subclasses.

Another problem with a subclass – especially from the point of view of unit testing – is that there is no way to create an instance of the subclass in isolation. By this, I mean that everytime I create an instance of the subclass, I am forced to have the parent class as well. In most cases, this shouldn’t be a problem, but I have run into situations where the parent class is just a landmine waiting to explode, with the default constructor not being explicit in stating its dependencies. So instant Kablaam!!! Or the parent class will load things you don’t really care about and make things slow in a test. There was this insidious test I ran into once, which extended a base test case, which did the same thing. About 7 layers deep. And the test itself didn’t really care about 3 or 4 of those layers, but had to jump through all the hoops and get everything because it was a parent class.

There are a few more issues, which are well documented in Effective Java item 16, “Favor composition over inheritance.”. I won’t bore you further on this, assuming I have convinced the skeptics about the problems with inheritance. If not, go read that book, and you shall be convinced. But then, I wanted to postulate on whether it was at all possible to have a programming language which does away with inheritance (As Noop proposes).

So when do we use inheritance ? To me, Polymorphism is about the only time when inheritance and subclassing is deemed appropriate. Be it having different subtypes or just plain old code reuse. So unless you want to have a base abstract class which has some methods defined (Like Shape with draw() method and Circles and Rectangles), inheritance is not really needed.

In Java, interfaces allow you to perform polymorphic operations with abandon, and convert between types. And interfaces don’t straddle you down with the requirement that you get the base class for every instance.

Also, if you use composition, then you can reuse code by using delegation. For example, you could define a Shape interface with a DefaultShape implementation. Now rather than subclassing a concrete type Shape, you could have a Rectangle which implements Shape. And if you wanted to reuse some code, let Rectangle take in a DefaultShape instance and just delegate to it when necessary. This offers multiple benefits. One, you are not tied down to getting things from the base class. In your test, you could pass in a mock, a null, whatever you want. The only problem is that this option is not viable if you don’t have an interface. If that is the case (or the thing you are subclassing is in a package outside of your control), then you are stuck doing inheritance the old fashioned way.

And this is (atleast the last time I heard the proposal) what Noop aims to solve. When you want to subclass, you tell the class what you want to compose. Regardless of whether it is an interface or not, it will create that class with an instance of your composition type. By default, all methods in the composition type will be available in the subclass, and it will delegate automatically, unless you override it. You get complete control over object creation, and this could potentially support multiple inheritance through this approach.

What do other people think ? It this feasible ? Am I missing something obvious when inheritance is the only approach and composition just doesn’t cut it (both right now and in the Noop proposal) ? Are you interested in Noop ? Drop me a line.

From http://theshyam.com

Published at DZone with permission of its author, Shyam Seshadri.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Walter Bogaardt replied on Wed, 2009/08/19 - 12:05pm

Inheritance isn't the problem. The problem, really is that at some point architectually, you have to look at your parent and children relationships and see if they need to be refactored. Interfaces present their own problem in that a class implementing the interface so if you end up refactoring your interface you have to refactor all implementing classes too. So the problem goes both ways.

Stevi Deter replied on Wed, 2009/08/19 - 12:30pm

I've certainly seen my share of inheritance hierarchies that make me think some developers should have their right to use "extends" taken away.

And favoring composition over inheritance is an important concept to well-designed OO code.

But I don't see how opting for delegation as you describe isn't trading one set of complexity for a completely different set. 

A lot of the problems you describe are just the result of poorly designed code. That code, written by the same developers, would likely be as poorly designed if it used delegation or decoration or whatever replacement for inheritance is chosen. 

Jilles Van Gurp replied on Wed, 2009/08/19 - 12:42pm

Inheritance is indeed not needed. I never use it other than to interoperate with stuff that requires me to use it. I'm sometimes tempted of course and the few times I made the wrong choice here, I found myself refactoring a few weeks down the road because the design was constraining me.

Basically, polymorphism you can & should achieve with interfaces. I.e. a dependency on a super class is still an implementation dependency. Which sucks for testing so use interfaces instead. So if you are using inheritance, your super type should in any case be an interface and not an (abstract) class.

As you point out, inheritance breaks encapsulation since you easily end up with spagethi type calls up and down the inheritance tree. There's loads of subtle room for bugs in the form of expected super calls that are never made, unexpected calls made by parent classes, etc. This is all pretty hard to test for so why risk all that?

Another risk is that lazy programmers start using inheritance just to share some common functionality. So, unrelated functionality inherits from some common class which provides a couple of utility methods. Inevitably there will come a moment where subclass S1 needs slightly different behavior than S2. So stuff is added to P that is only needed by S1. This can be new methods, some if statements, etc. 

A simple rule of thumb here is that if the inheritance relation does not express a clear is-a relation, you are basically dealing with a poorly designed inheritance hierarchy. As soon as is-a becomes uses, you should be using delegation and not inheritance.

It's a slippery slope but composition relations are more flexible:

- you can modify them at run time

- you can isolate the class for testing (using stubs or mocks), which you should do BTW.

- you can hook up any implementation, including anonymous classes.

Combined with interfaces and a simple proxy pattern, you can basically convert any inheritance relation to a delegation based one.

Josh Marotti replied on Wed, 2009/08/19 - 2:24pm

Doesn't delegation give you the same problems as inheritence? 

And if you are using composition heavily, you are tightly coupling your code, which is another big no-no...


And polymorphism that also has delegation??

 

It sounds like removing inheritence will lead to serious spaghetti code...

Rogerio Liesenfeld replied on Wed, 2009/08/19 - 3:02pm

Inheritance is overrated, of course, and this has been known for years. That doesn't mean it should be avoided at all costs, though. It's mostly implementation inheritance that tends to cause problems, not interface inheritance.

Another great advice from Effective Java is the next item (17): "Design and document for inheritance or else prohibit it". Unfortunatelly, not many developers seem to follow this.

Bruce Fancher replied on Wed, 2009/08/19 - 4:24pm

Inheritance is just an automated, although in Java somewhat limited, form of delegation. I wish something like what is proposed in this paper had been added to Java long time ago:

Automated Delegation is a Viable Alternative to Multiple Inheritance in Class Based Languages

Abstract

Multiple inheritance is still a controversial feature in traditional object-oriented languages, as evidenced by its omission from such languages as Modula-3, Objective C and Java™. Nonetheless, users of such languages often complain about having to work around the absence of multiple inheritance. Automating delegation, in combination with a multiple subtyping mechanism, provides many of the same benefits as multiple inheritance, yet sidesteps most of the associated problems. Automated delegation could satisfy both the designers and the users of class based object oriented languages.

In this paper, we discuss why automated delegation is desirable. We also present Jamie, a freeware preprocessor-based extension to Java that offers such an alternative.

Erik Post replied on Wed, 2009/08/19 - 4:33pm

Hm, it's just a shame that Java sucks so badly at composition. It has no multiple inheritance, fair enough. But if you want to share code across classes without using inheritance, you have to resort to things like external helper classes, which is arguably even worse than inheritance. It's a shame Java is lagging behind the rest of the world so much in so many ways. Yay for Scala's traits!

Mike P(Okidoky) replied on Wed, 2009/08/19 - 6:16pm

People that think OO isn't needed at all are likely the ones that write short bits of code for each of their purposes, like in a php program, where each web requests needs just a bit of streamed serial type of code. Open db, read data, wrap in html, done. But before you know it you end up with a whole bunch of code duplication and spaghetti code. I think there's no excuse for not having OO in a language. Even in php, I've created a nice set of classes after which I could take a project to whole new complexity levels without things becoming unreadable or convoluted.

"Only for polymorphism", and yes applications beyond trivial simple toys use it all over the place.
So if some don't like OO, think it's overrated, or think we can do without, why search for ways to do multiple inheritance?

I think multiple inheritance is very sorely missed. I've missed if from day one, because in C++ I was able to isolate and compartmentalize a lot of code. Multiple inheritance allows you to organize in an aspect programming kind of way. I think Java should introduce true multiple inheritance, and none of delegation crap.

The problem with delegation, be it automatic, preprocessor generated, or whatever scheme is concocted, is that the code that's being delegated to, doesn't have the right 'this' reference. The delegated inner embedded object is not the outer object that implements the interface. That alone in my opinion cripples delegation type constructs.

I think we should just come to terms that multiple inheritance in C++ was good, and we should introduce all those features in Java. The potential to write more localized code is really great.

James Jamesson replied on Wed, 2009/08/19 - 11:17pm

Wow, what other nonsense are we going to see on Javalobby next I wonder. Inheritance is overrated only if you do not know when to use it my friend. I have just decided to remove javalobby from my feed. Happy rumbling all.

Jeroen Wenting replied on Thu, 2009/08/20 - 12:26am

Inheritance has its place, so does composition. To state that one is inherently and universally superior over the other is typical of what I see schoolkids post on forums ever more often, asking "which is best, A or B" (meaning, which is to be used always instead of the other) where A and B are complementary rather direct alternatives.
It's incredibly naive, and while such ideas are somewhat acceptable from newbies of highschool kid age someone who calls themselves a professional and an adult should know better.

Alex Eagle replied on Thu, 2009/08/20 - 11:46am

@Bruce Fancher: Thanks for the link to the paper! That will be really handy for us to get the implementation right in Noop. I'm glad to see the idea of polymorphic delegation isn't entirely new, and I agree it would have been really helpful in Java.

@Jeroen Wenting: I think you missed the point. We're discussing a construct that's neither inheritance or composition. In a language that has only those constructs, yes, there are tradeoffs.

The idea here is that you delegate to a class, but ALSO are a subtype of the class. To client code, the delegate looks the same as an implementation-inherited subclass. But, you can test the delegate in isolation from the supertype.

@fortknox: You're not coupled to the composed class, just to its type. It's no different from having a dependency on a certain interface. Yes, if the interface changes, you will have to change classes that implement the interface, but with automatic delegation, as long as you didn't override any methods from the delegate, there are no changes to make in the delegating class.

@Mike P: That's a good point that the 'this' reference in the delegated code doesn't refer to the delegating class. That's the only thing I can think of that becomes impossible with delegation. It's interesting that you think that cripples the construct. Would that kill a language that didn't provide implementation inheritance, only this sort of polymorphic delegation?

Here's an example where this would be a problem:

class Super() {
  public void draw() {
    print();
  }
  public void print() {
    sout("hello");
  }
}
class Sub(Super s) delegatesto s {
  @Override
  public void print() {
    sout("world");
  }
}
new Sub(new Super()).draw();
That prints "hello" where we wanted it to print "world". Definitely a problem to solve.

Stefan Langer replied on Fri, 2009/08/21 - 5:29am

It's time to read up on some basic principles about programming in OO:
- Open-Closed Principle (Use Wikipedia for definitions as I'm to lazy to do it for you)
- Liskovs Substitution Principle
- Law Of Demeter Just to name a few...

And by the way Traits or Mixins are a different form of inheritance.

If you do not want inheritance maybe you should forget about OO and start doing functional programming as I see polymorphism as one of the essential features about OO.

To put it another way: It is not the tool that is broken it is the guy using it the wrong way.

Shyam Seshadri replied on Fri, 2009/08/21 - 4:56pm in response to: Stefan Langer

Alex addressed most of my concerns, but from what I am seeing in the replies so far, it seems that people think I am saying one of three things :

  1. OO sucks
  2. Polymorphism sucks
  3. Some combination of the above two

Let me clear things up. I love OO. I depend on polymorphism. There is no way to write maintainable, extensible code without either, and my interview questions usually revolve around how people use polymorphism.

But if someone reread that article, you would see that we are not saying we will "Do away with inheritance and polymorphism". But rather, we are trying to suggest a way to have Polymorphism, through a combination of automated delegation rather than the typical class inheritance that is prevalent in Java, some of which are highlighted in the article.

Furthermore, nowhere do we suggest breaking the Law of Demeter (which is, according to me, a fundamental tenet of writing testable code and something I have covered in depth in some of my other blog posts).

And exactly, it is some guy using it the wrong way is what we are trying to solve. Why should it be possible to dig yourselves into a hole because of someone else? We are trying to figure out if there is a way to stop people from abusing the typical class inheritance.

Mohamed El-beltagy replied on Sat, 2009/08/22 - 3:29am

I would like to thank the writer of this article for letting me know that in Effective Java they prefer composition over inheritance (I haven't read Effective Java yet and I think that I'm missing a lot).

But I do have the following two comments:

1- I think that the article's title is exaggerated. The reason why I think so is the next comment.

2- Inheritance is a design pattern. Composition is another design pattern. According to wikipedia:

In software engineering, a design pattern is a general reusable solution to a commonly occurring problem in software design. 

 So, it all depends on the situation at hand. There is nothing as (Best solution for all situations). But there is: Best solution for certain situation.

Sometimes, inheritance will do just the job and yet will be plain quick and easy solution. Actually, even for certain situations, inheritance is the only solution. i.e., consider the JComponent, JField, and JPasswordField components in Swing.

For some other situations, composition is the best if not the only solution.

 

May be as seen and have been proven by most people composition is cleaner, easier, and is much better than inheritance most cases. But that does not mean that inheritance should not be used or is 'overrated'.

Again, as I have been commenting on most of Java Lobby articles:

It's a matter of when to use what.

Dapeng Liu replied on Mon, 2009/08/24 - 2:29am

Well i believe annotation has solved quite a bit of problems related to inheritance & composition

Frank Silbermann replied on Mon, 2009/08/24 - 10:58am

A class that can be subclassed becomes part of the API.  When you change an API, you break code.  Does that mean we should not have APIs?

Brian Remmington replied on Tue, 2009/08/25 - 7:45am

One pattern that I use frequently is the Template Method pattern. This pattern is nicely implemented using inheritance in Java. While I can imagine alternative approaches, they seem slightly inelegant by comparison.

 I'm sure that the answer to the question "could we cope without inheritance?" is bound to be "yes". To remove the tool from one's toolbox seems rash, but, of course, to misuse a tool can be dangerous. If you can't use a tool properly then it's best leave the job to an expert. You could hurt yourself and others.

Is inheritance overrated? I think it's easier to answer the question "is inheritance misused?". The answer is "yes", but then the same is true of most powerful, flexible programming constructs. That's not a reason to discard it.

 

Regards

Brian

Coffee Jolts replied on Tue, 2009/08/25 - 1:18pm

I could drink water until I puke. That doesn't mean water is overrated. Anything good can become bad if overdone. Inheritence is no different.

Markus Hanauska replied on Wed, 2009/10/14 - 7:14am

People trying to improve programming languages make one big mistake when talking about how things might or should be - they concentrate solely on the programming language and maybe some abstract concepts. What else should they think about, you ask? Computers! No matter what language you use, no matter if it is compiled to native code, to intermediate code (executed by a virtual machine, interpreted or JIT compiled), or stays program code and is interpreted directly (like a script), in the end there is some hardware that must execute this code.

 The greatest concept on earth is useless if it is so far away from how todays  CPUs work that the program is 100 times slower than anything else. Honestly, if adding a feature to your favorite language would cause the whole language to slow down by the factor 100, do you really insist on having this feature? A lot of people think of applications as stupid UIs that perform tiny actions when a user clicks on a button. Yes, those applications do exist. But there are also applications that decode MPEG2 video streams in realtime and slowing them down by 100 means now they cannot even decode a MP3 audio stream in realtime anymore.

 I can already hear what you think now: Why not just mix in some more low level code into your app in that case? If I write an app in Java, I usually don't do it because Java is the greatest language on earth, but because I want to cover as many platforms as possible. Mixing in native libraries means I add a platform dependency and then I can as well not use Java, but something that compiles native in the first place. And I don't want to use a native library to decode MPEG2 video, I want Java to become fast enough to do this within 100% pure Java. The goal for every language must be to become as independent of rakes like using native libraries for anything that must be fast.

 Now considering how CPUs do work, or computers (including operating systems) in general, delegation or composition has big disadvantages over inheritance. When creating an object from a class, no matter how its inheritance tree looks like, in the end a single malloc call is done to reserve memory for it and each method call is an indirect function call; including one constructor call, and that's it. This is effective, this is fast.

If I use composition or delegates there is one malloc call for my object itself, and one for every delegate or object I need for composition. This alone makes apps noticeably slower (malloc is not a fast operation! Never has been, never will be). It also adds memory overhead, as malloc rarely allocates exactly as many bytes as you requested from it, usually it internally allocates somewhat more memory. Further ever method call is now not just a single indirect function call but it is now a indirect function call to another indirect function call (that might be one to another indirect function call and so on). So the method call overhead doubles at least (it may tripple or quadripple) and already right now a Java method call is significantly slower than a C function call (again, always has been and always will be, it's not possible to do it in another fashion). Further the constructor must again call the constructor for every delegate or composition object, making constructor calls anything from somewhat slower to x times slower (if the other constructors also init delegates/composition objects, that will then do the same and so on and so on).

 A standard API that heavily depends on delegates or composition has a pretty much unpredictable runtime behavior in regards of CPU time a call needs and in regards to memory consumption. Adding this to the case that JVMs are already pretty much unpredictable today (as they asynchronously perform whatever magic in the background whenever they feel like it) you end up with a language where the same code on similar system once runs at the speed of light and once crawls like a turtle. You have this already now for Java, but delegates and composition multiply this issue by an unpredictable factor. And no, the JIT cannot straight things out. A JIT is not like a database engine, where you send a SELECT and the engine will do all kind of magical optimization to find out how to perform the select in such a way that it is fastest and at the same time needs least memory. That is not how Java or comparable languages work.

 There are problems with inheritance, no doubt, and they can somitems be solved by delegates or by composition, but saying "drop inheritance, solely go for delegates and composition" will raise issues that are much more annoying than all the inheritance issues ever have been. Mixins are no solution either, IMHO they make every negative aspect of inheritance (maybe I should say implementation inheritance here) much worse just on a different level.

 The biggest problem I see in Java is that you have interfaces from that you can "inherit" an interface and there are classes from that you can inherit "interface and implementation". Why can't you just inherit an interface of a class without inheriting the implementation? Why can't you just treat every class as an interface if you like? This alone would make the whole Java world so much easier already.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.