Dec 17

Composition vs. Inheritance (hint: usually not inheritance)

Category: Java, Programming

A co-worker of mine recently made the comment that a lot of times you see Java code using inheritance where composition is a much better solution. This is very true and worth pointing out.

Basically the problem boils down to this: Although both inheritance and composition form a relationship between objects inheritance creates a strong bidirectional relationship between two objects while composition provide a loose coupling.

Composition Is Better

Probably the easiest canonical example would be Thread in Java. When you create a thread you have two options:

  1. Derive from thread and override the run method. (inheritance)
  2. Create a Runnable. and pass it as an argument to the thread’s constructor. (composition)

In both cases you have created some logic that runs in a thread, but if I inherit from thread I have married the logic I want to execute with the execution mechanism of the Thread.

  1. If I want to use the business logic I have to bring in thread as well. In the worse case I have to actually call run to make the code do anything which means I can’t interact with any of the business logic synchronously.
  2. Your business logic can’t inherit from anything other class even if there is a more appropriate is a relatioship.
  3. If I want to test the code I can’t test the business logic without running the thread which is going to lead to threaded unit tests which are at best a hassle and at worst ineffective.

Composition decouples all these concerns. I can run the logic in a thread, synchronously, or queue it up for a thread pool. If I want to use a new threading library I don’t have to change the code, code that interacts with it, all the associated tests, and then re-validate everything (assuming no one else now depends on my highly coupled interface in which case I’m screwed).

And this doesn’t simply apply to threading. Containing environments, serialization libraries, wire protocols, etc. are all better when interacting via clearly defined interfaces rather than closely coupled to implementations.

Inheritance Isn’t That Great Anyway

Even in cases where inheritance might be more appropriate it still kind of sucks. One of the biggest problems with inheritance is the difference between abstract methods and non-final methods. Well let’s look at Thread again. As of writing this thread has over 20 public methods not counting the ones it inherited. None are abstract and over 10 are non-final. These methods also provide a number of different purposes from executing logic to providing state information to fiddling with the class loader. So if I want to create a thread which ones can I override? Of those how clear is it what will happen if I override them and what dependencies Thread itself may have on them?

Keep in mind this is a pretty well written class. How many classes have you written where you really thought about what should be final, public, private, etc and documented exactly how a derived class would interact with your class? Did that documentation stay up to date? Did you maybe make everything protected “just in case” thus making it 100% impossible for anyone in the future to know what you intended them to do? Prior to @Override you wouldn’t even know if one of the parent methods was renamed, thus rendering your method completely useless( you are using @Override right?).

Contrast this again with runnable which has one purpose (to run something) and one method (called run). It has no existing logic so the semantics of what to do are 100% clear. You implement the interface and override every method. If the interface changes you always get a compile error and update your class. done.

Even Object which is probably the best example of a really useful inheritance relationship, has hashValue and equals. Collections rely on both of them and the only thing you can usually rely on is that someone didn’t bother implementing them because they aren’t abstract.

I’m personally of the opinion that the best way to inherit is where everything you are supposed to provide is protected and abstract and everything else is public and/or final, but of course even then

  1. What a pain
  2. It still isn’t clear in which context it is safe to call which methods on the parent class (i.e. it may cause deadlock or inconsistent state)
  3. That’s writing an interface so maybe you should just do that.

Not All Languages Are Java

Finally, it’s important to note this advice doesn’t apply directly to all languages. Although avoiding close coupling is always a good idea, inheritance semantics and testing/re-factorability/etc are not the same in all languages. For instance, C++ has template inheritance which can in some cases decouple a class from it’s parent class(es) and multiple inheritance which means mixins don’t have to be done by composition + interfaces, and ruby has module mixins and the ability to reopen classes which makes inheritance a much more loosely coupled concept than in Java.

The Author

Michael Smit is a software engineer in Seattle, Washington who works for amazon


Comments are closed.