Inheritance is a widely used technique in almost any project, but if it is not well applied it can lead to poor designed code with unnecessary coupling between classes.
Composition usually offers more flexibility combining objects of other types.
Learning the key points of each technique can help to create more flexible designs.
Inheritance
Inheritance must be used when you need to specialize the behavior of a class, this means that you should not use inheritance just to reuse your code, any subclass is a special case of its superclass.
A typical example is:
The key point here is that a Bird is an Animal and that is the relationship your classes must have in order to use inheritance. Another example could be Person and Employee, Vehicle and Car, etc.
To generalize: when a class B inherits from a class A, we often say B “is-an” A. This means that instances of B are in fact instances of A.
Python and multiple inheritance
In python we can use multiple inheritance, but it’s very important to understand how a method is looked up.
Let’s put an example with multiple inheritance.
This example may look weird but keep in mind that every class in Python 3 has now the same common ancestor so you will have a CommonBase (actually is `object`).
This is scenario is called the diamond problem and it looks like this:
Given the example above, what do you think you’d get if you run:
>>> MyClass().method()
The answer is Base2
How did we get this ???
We got Base2 because of the method resolution order (MRO) implemented in Python 3 (in python 2 MRO follows the rule left to right depth first , in which MyClass().method() => CommonBase).
How does the MRO work ?
MRO is based on C3, which was the MRO of the Dylan programming language, and it consists of a linearization process. MRO is based on a recursive call over the base classes, looking up each parent class to create a sequence of lists, removing duplicates and conserving the order. Once this sequence of lists is created it computes a left-to-right rule to merge them all.
In our example, Base2 is the first class that contains method method
Composition
In composition we hold references to another object/s because the relationship between classes is different.
Let’s suppose that we have two classes: Car and Tire.
We can quickly check that there isn’t any is-an relationship but a has-a one (a Car has a Tire, a Car is not a Tire), that’s key to understanding what composition is. We must not use inheritance if we detect this kind of relationship between classes.
How is composition implemented ?
In this example we can see that there is no inheritance but we are holding a reference to the salary in the Employee class because both classes have a has-a relationship.
So far this looks good but if we pay attention to this implementation we will see some coupling between classes because, if we would like to replace Salary with another class we will also need to change the Employee class, so a good advice is to combine composition with inversion of control, like this:
Composition and inversion of control usually leads to less coupled code which is easy to maintain, extend, test, etc.
Conclusion
Inheritance and composition are quite different concepts and they respond to different relationship between classes.
If classes have a is-a relationship then inheritance is the right choice, if they have a has-a relationship then it’s a composition.
Even if inheritance is the right choice you will still be coupling classes together, for that reason composition often produces more flexible designs, and it’s even better when combined with other patterns like inversion of control to decouple the classes.