You are tasked with building an application that deals with cars. You are off designing the classes for cars unaware of the looming dangers of inheritance.
Every car has a drive functionality, the driver floors the accelerator and the car moves.
So, you design a base class called Car which has a function drive().
 method in this base class so that all the sub-car classes inheriting from this base Car class get it as it is common functionality. You feel very good about yourself for writing reusable code.
The client wants a car that can drive with petrol and another on electricity.
You create two classes for these:
.
.
 and Charge().
 method will now have to be duplicated across all the car classes except Toy Car class. You can see how we are now heading down a slippery slope right?
What we are seeing is a pattern of IS-A relations; and electric car IS A car. This is the root of the problem here. We should get rid of so many IS-A relations and compose a car object with HAS-A relations. HAS-A relations can be interpreted as a car HAS A drive behaviour. We can create separate interfaces for each behaviour:
![Behaviour interfaces](/assets/img/articles/2018-07-23-Composition-over-Inheritance/behaviour-interfaces.webp
We can now compose the Car Class with these behaviours:
![Car behaviour composition](/assets/img/articles/2018-07-23-Composition-over-Inheritance/car-behaviour-composition.webp
What this means is that a car object is composed of the above functionality. They are not concrete implementation, rather, they are abstractions to allow any behaviour to be plugged in depending on the need. There can be any number of concrete implementations of the behaviours:
![Refuel behaviour implementation](/assets/img/articles/2018-07-23-Composition-over-Inheritance/refuel-behaviour-implementation.webp
Every Refuel Behaviour class must have a Refuel method. The NoRefuelBehaviour class is also a Refuel Behaviour in the sense that it states the absence of this behaviour in the object.
We take a concrete implementation of the interfaces we have in the Car class and inject them to compose cars with mixed functionalities.
The composition can look like this:
Petrol Car = SimpleDriveBehaviour + RefuelPetrolBehaviour + NoChargeBehaviour
Electric Car = SimpleDriveBehaviour + NoRefuelBehaviour + FastChargeBehaviour
Hybrid Car = SimpleDriveBehaviour + RefuelDieselBehaviour + SimpleChargeBehaviour
Toy Car = NoDriveBehaviour + NoRefuelBehaviour + NoChargeBehaviour
As you can see, this way the software becomes much more robust; the model supports pluggable interface implementations and has no code duplication. Now when the client comes up with a new car type, you can just compose it to match the requirement; no inheritance hierarchies to manage.
Conclusion
We have looked at a very simple example in this article. Even in this simple model, the inheritance tree broke and we looked into how to fix it using composition.
You might be thinking, but Tanveer, isn’t inheritance one of the pillars of object oriented programming, are you telling us not to use it?
All I’m saying, like many other developers, is to favor composition over inheritance. Currently I’m working with Golang most of the time, and it does not support inheritance. It forces developers to use composition instead. This leads to a much better software design. I believe it wouldn’t be going too far to suggest that we don’t even need inheritance.
Article Photo by Kelly Sikkema