DEV Community

Cover image for Why composition is superior to inheritance as a way of sharing code

Why composition is superior to inheritance as a way of sharing code

Rui Figueiredo on February 22, 2017

Inheritance is one of the pillars of Object-oriented programming (OOP), the other two being polymorphism and encapsulation. Inheritance is therefor...
Collapse
 
jim_ej profile image
James Ellis-Jones

The article misses out polymorphism which is the key use case for inheritance. You can't use a class which is composed of another class in the same context as the second class.

It also overstates issues with testing and lack of clarity of child classes. It requires a little thought but it's easy enough to do constructor injection with class hierarchies.

A good example where inheritance is essential is in presentation: it's vital to be able to process a range of similar but different visual components with the same code. Such visual components typically share a lot of internal code so interface polymorphism is not a good fit.

Like many issues, inheritance and composition are both valid approaches in different context rather than one being 'better' than the other

Collapse
 
ruidfigueiredo profile image
Rui Figueiredo

Hi James,

The article starts by distinguishing implementation inheritance from interface inheritance. And specifically the use of implementation inheritance as a way to share code (this is where you have a base class and you derive from it to access its methods, no polymorphism is used here).

If you are talking about inheritance being essential in presentation specifically to take advantage of polymorphism them I agree, but that was not the point of the article.

Collapse
 
jim_ej profile image
James Ellis-Jones

Hi Rui

Thanks for your response. My apologies that I misunderstood your limitation of the point you were making. I think we can then agree that in cases where you need polymorphism together with sharing code, inheritance has it's uses, but where polymorphism is not relevant, composition is preferable.

Collapse
 
cullophid profile image
Andreas Møller

Do you have an example where subtype polymorphism cannot be replaced by composition and interface polymorphism?

Collapse
 
pnambic profile image
Daniel Schmitt • Edited

One example would be a requirement to enforce behavioural invariants (one of the aspects of the Liskov substitution principle aka the L in SOLID). The compiler can't verify that you followed some rule in the dev manual that says, "all classes implementing IDependency must implement method x() by calling _dep.x(), and only _dep.x()". But, if you express that rule through final/non-virtual methods on a base class instead, then it can, and as a bonus it's self-documenting.

Another example, from a maintainability point of view: imagine having 42 classes implementing IDependency and a requirement to extend that interface with a new set of methods. Would you rather touch one or two files, or all 44 of them, even if the changes are mostly trivial? Java 8 added inheritance of default implementations to interfaces to support that exact scenario.

Lastly, imagine you would like to inherit several levels of bases because your problem domain is hierarchically structured, you need to conclusively demonstrate correctness in some parts, and want to leverage the type system to do that. So you end up with something like InvoicePaid (detailed business rule level) inheriting from TaxAccountable (legally required, with external code audit) inheriting from Financial (security rules about event routing) inheriting from Event (shared technical infrastructure).

Interface inheritance can usually deal with the correctness issue, because while the relevant behaviour depends on the type, it's typically implemented external to it (unlike in the first example), but imagine what a leaf class implementation would look like if these were more than just marker interfaces. And they will be, and you'll have lots of leaves.

What this boils down to is: every language supporting OO implementation design supports some form of implementation inheritance and also some form of composition (AFAIK), and there are good reasons for that. Traditionally, inheritance has been oversold by OO teaching materials, and overused as a result, but avoiding it completely is overreacting.

Collapse
 
danderson profile image
Dale Anderson

Inheritance creates very strong coupling between base classes and derived classes. It's very difficult to break this coupling, i.e. you become somewhat locked in to your original conception of how your system is structured. In long lived systems, this inevitably changes.

In my experience, the very dire consequence of this is that as your system grows in size and complexity, base classes tend to accumulate many aspects that may or may not apply to all of its derived classes. It's extremely difficult to reason about systems with base classes that are over several hundred (thousand?) lines long.

Composition is a far more flexible approach that allows a more fine grained approach to decomposing the various elements of your system.

OOP looks great when you consider the simple examples that they teach in computer science 101, like cats being mammals that are in turn all animals, or a teacher and a student being specializations of a person. However, it is extremely difficult to scale to today's large size systems, mostly due to the strong coupling I mentioned.

Collapse
 
exadra37 profile image
Paulo Renato

Hi Rui,

Excellent and simple article about Inheritance vs Composition.

After I read the book Pragmatic Programmers and Clean Code I gradually changed the way I code to the point I don't use Inheritance unless forced by the Framework I am using.

Composition === Explicit Code

Inheritance === Implicit Code

Nowadays I am 100% in favour of being Explicit over being Implicit.

Collapse
 
eriksk profile image
Erik Skoglund

"automated copy & pasting", I'm going to start calling inheritance this at work and see what happens!

Collapse
 
lluismf profile image
Lluís Josep Martínez

Would be much more convincing with examples of real classes instead of meaningless ones. IMHO both inheritance and composition are useful, it just depends on the problem. Classic examples of Shape, Triangle etc. are much clear with inheritance.

Collapse
 
ruidfigueiredo profile image
Rui Figueiredo

They might seem clearer but that's just if you think about inheritance as an IS-A relationship. For example it seems right to say "A Triangle is a Shape".

Even though it sounds right, you get very little from that if you look at inheritance in OOP for what it is. It's just "... declaration of
methods and variables in a subscope and it has nothing to do with ISA whatever, and the notion of ISA can be very confounding."

Not my words, they are Robert Martin's (known for the SOLID principles and the Agile methodology). He said them in this interview: hanselminutes.com/145/solid-princi...

Here's the transcript: s3.amazonaws.com/hanselminutes/han...

IS-A comes form AI's knowledge representation field, where there are algorithms that take advantage of that information to perform inferences (e.g. Semantic Nets).

In OOP there's no inference, so in the end a "Triangle" inheriting from a "Shape" class just means that the public and protected members of Shape are available in Triangle.

Inheritance is useful in some situations, but not when think about it using the "IS-A" perspective.

Collapse
 
lluismf profile image
Lluís Josep Martínez

The forget about the IS-A origin, in the real world a triangle is a kind of shape as well as a financial agent is a kind of person or a drug order is a kind of order. When defining entities belonging to a hierarchy I find it more natural to use inheritance instead of composition.

Collapse
 
jluisestrada profile image
JLuis Estrada

Its because you are seeing inheritance as a way to share functionality, when the purpose is to share characteristics.

Collapse
 
danieldubovski profile image
Daniel Dubovski

There is no one true purpose to a design principal, as things evolved a lot along the way.
Inheritance is used in a lot of languages , with a lot of specific features, all designed for different use cases (of course some of them overlap).
That being said the theory and practice of Inheritance vary, and it is often used to share functionality, because it's relatively ease and clean to use.

Collapse
 
jluisestrada profile image
JLuis Estrada

Obviously you can use inheritance the way you want. Is not restrictive at all other than a few rules that change from language to language.

But overall, that's the key feature of inheritance: share characteristics among a descendants in order to create a cohesive domain.

Thread Thread
 
danieldubovski profile image
Daniel Dubovski

I think the problem here is the terms we use.

I would love to see a reference to back you up. programming is prone to opinionated discussion, so some background to the opinion you hold might be helpful.

If by characteristics you mean properties, then I disagree, as a characteristic can also be a function (or a capability).

Anyway, I might be wrong, but check these out:

www2.latech.edu/~box/ase/tp/Per%20...

en.wikipedia.org/wiki/Object-orien...

esug.org/data/Old/ibm/tutorial/CHA...

Collapse
 
lex_olival profile image
Alexandre Olival

Awesome article. Finally all those code samples I've skimmed through that seemed to have a redundant Interface and an immediate implementation right next to it are all clear!

Collapse
 
stasgavrylov profile image
Stas Gavrylov

Nice article, thank you. I never fully understood the "composition > inheritance" concept, but now it clicked :)

Collapse
 
sebastianr1982 profile image
Sebastian Rapetti

Dependency injection instead of inheritance? In the second code snippet I see a dependency injected and a method of this dependency exposed through the method Method().

Collapse
 
sebeichholz profile image
Sebastian Eichholz

Great article.
Thank you for writing it!

Collapse
 
deen_john profile image
Deen John

Coding with Inheritance is like predicting future