I received an email from a friend of mine recently who was asking for my opinion on Composition Vs. Inheritance. My friend had just come across the phrase “favour composition over inheritance”. Now my friend said something interesting that I perhaps hadn’t considered or appreciated before, he said “…I am struggling to understand how one can be compared to the other…”. Now, at first glance this looks simple, but actually, I stopped and thought about it. How can they be compared to one another? In this post I state that they are not comparable as they solve fundamentally different problems. I assert that whilst inheritance can be used to solve the problems composition solves, that is a side effect of inheritance.
I was going to do an aside here about my views before and after replying to my friend, but honestly, that’s the post. Suffice to say, my viewpoint has not changed, my reasoning behind my viewpoint has increased and developed significantly.
Let’s have a few brief definitions first, these are simplified and I’m not going to go into too much detail here.
Inheritance is where a class either extends another class or abstract class, thus obtaining it’s set of behaviours and state and being classed as a version of that. Or it is where a class implements an interface (contract) which defines which behaviours should be expressed and leaves the implementation down to the implementing class. An example is in a game, an NPC would implement a character which has abilities such as movement and state such as health or stamina.
Composition is where a class has various class level variables of other types. The class then calls these variables and delegates certain behaviours to them. An example of this can be found in pretty much any business software such as web services, there would be a class listening on an endpoint which would delegate to various other services which in turn might delegate to database access classes or various other classes.
What problems do they solve?
So, in what way wouldn’t these be comparable? Let’s consider the problems that each solves.
Inheritance fundamentally addresses the problem of heredity and ancestry, it also addresses the problem of classification and grouping, a good example of this is in Biology and Zoology.
Composition on the other hand deals with the problem of needing to isolate different behaviour to things dedicated to that behaviour. Why would we even do this? Let’s look at how composition came about. In the beginning there was a single master file, in this file was a massive sequence of code which ran. There were goto’s and breaks everywhere to try and direct the execution to a different piece of code. This was complex, difficult to read and left many developers thinking wtf. Then there were functions, ways of splitting out different behaviour into small readable blocks which were easy to reference, we were able to classify and group our code into something meaningful, for example a function to calculate interest in a consistent and standard way. Then we started grouping these into separate files, again to make it easy to read and understand, and eventually somewhere in there, for OOP at least, classes were defined and developed.
Composition is about making code easy to read and providing a single source of truth for key business functionality.
Inheritance is about indicating ancestry and heredity and ensuring that derivatives of one thing behave in similar ways.
So what’s the problem?
The problem is that it is possible to achieve an element of composition using inheritance. Inheritance is able to bestow a class with certain behaviours in the same way delegation does. The issue is that this is no guarantee of addressing the problems that composition solves, single source of business truth and ease of readability through compartmentalisation of the code. So why do developers often choose inheritance when they should be using composition?
There are a few reasons for this:
- Taught incorrectly about the two
- Bored and wants to do something fun
- Ego stroking
The first reason is plain and simple, being mistaught leads to life time habits that are hard to break.
The second reason is where things get interesting. Let’s be honest, once you’ve written your 200th CRUD system or 500th REST API, things are gonna get boring, it’s all the same you see. Controller -> Service -> Repository. That there is the basic pattern to solve pretty much 70% of the problems that business have, between each one there is a mapping layer to translate from one form to another and in the service there will be some business logic which sets certain state based on certain rules. There is nothing interesting here, there is nothing fun here, it’s all boring (and that’s good!). A suitably senior developer needs to stretch their legs, they need to play and they need to be learning, that is the life of a Software Developer. So what is a developer to do when they’ve solved the problems and are just cranking out the same stuff? They play, the do inheritance. I’ll be the first to admit, creating a sexy framework using inheritance and having it so extensible that it’s almost like magic is happening is really fun! Like seriously fun, it satisfies a certain need to create complex and elegant systems out of nothing, it requires a level of intelligence to follow and understand, and that makes us feel good. It makes us feel like we are truly Software Engineers. The problem is that it’s just over complex for the situation. The solution being built doesn’t need a framework, it doesn’t need all the inheritance. Developer feelings are not the purpose of products.
The third reason is an extension of the second, except that a developer does it to show off, purposefully making it difficult for other developers to read so that the original author can feel clever. If you are this person, then on behalf of every developer everywhere please accept my sincere thanks, and now please fuck off somewhere else and ruin someone else’s productivity with your pompous technical masturbation.
There are probably more reasons why a developer uses inheritance when they should use composition, if you know one then please comment below. If you’d like to object to the last paragraph then also, please comment below.
Help me choose
I have some simple rules that I apply which help me determine whether or not the situation truly calls for inheritance rather than composition. If one of these rules is met then inheritance is on the table as an option, if not then it’s not.
- If I use an interface then I must never, ever, care about the order in which the implementing classes run. I must never need to cherry pick only one type of that interface and I must never need to cast to different objects depending on which implementation of that interface I am dealing with.
- If I use an abstract class or an interface then I should always, without exception, be able to substitute any other implementation of that in to achieve the same desired outcome. I should never care which one is substituted in.
There are some consequences of these rules which should give you an idea as to whether you are doing the right thing. These correspond with the associated rule number above.
- I probably have some kind of list/set that I iterate over in an unordered way, I never care about the implementation and I never try and impose accidental order such as by placing one thing in the list first (the very instant I do this then I’ve misapplied the interface and it shouldn’t be there at all)
- I’m building some kind of generalised framework, in which case the framework should be able to be a stand alone library completely external and it should not change due to business rules. The library should be applicable to multiple different software instances and projects.
In the case of 2, if it’s something like an interface on top of the DB access then that simply doesn’t wash with me. It’s just completely unrealistic! The excuse often used here is that you might want to change the underlying database at any point. Honestly! This is never, ever, ever going to happen, and if it does then the chances are you will need to change a load of code anyway, all this does is add extra complexity to consider!
These days we live in microservice territory which means the need for frameworks should be zero within an individual microservice. A good rule of thumb I apply is that if a microservice is done well and understood well (implements a discrete vertical slice of well known business functionality) then it is cheaper to rewrite it than it is to refactor it. This unfortunately means that most of the OO rules we have go out of the window as they are just overkill for what we are trying to do.
I’m fairly passionate about all of this, mostly because all of these things are things I’ve discovered the hard way through my own coding. I used to be a major proponent of using inheritance all over the place, but as I’ve become better at what I do and as I’ve experienced more I now code for human readability. If code looks astoundingly easy to refactor then it’s probably really good code and shouldn’t be refactored because there is no refactoring that can be done to improve how easy it is to read that code. The compiler does all the optimisation these days and most software get’s double compiled through various forms of bytecode and JIT compilation, optimising for the computer just isn’t required, and if it is then it’s not usually solved by things like OO, but through things like pooling and caching.
To conclude, my friend is right, inheritance and composition should be comparable, they solve fundamentally different problems. But because you can achieve the effects of composition using inheritance, inheritance get’s misused. So it’s less about choosing between the two, but more about understanding the two different problem domains they solve and using the appropriate tool at the time. I suppose a good analogy would be using a laptop to hammer a nail in, this is equivalent of using inheritance to do the job of composition, it’s costly, messy and just not right.
If you want advice or an initial free consultation on some of this stuff and some of the problems that you’ve got with code quality within your business then please get in touch: firstname.lastname@example.org is the email you need, drop me a mail and I will get back in touch with you.