Inheritance and composition are two choices we have in the case of object-oriented programming practices to follow to have code reusability in place. The concept of code inheritance comes into the picture when you want to inherit some properties and functions from the base class and have some additional properties and functions in the derived class.
Below are the broader types of Inheritance supported in any object-oriented languages. I am taking c# as an example below.
- Single Inheritance: In this type of inheritance, there is always one base class and one derived class. For e.g., Class B inherits Class A. In this case, all the capabilities (all public and protected members of the class) of Class A are inherited in Class B.
- Multilevel Inheritance: In this type of inheritance, more than two classes are involved. For e.g., Class C inherits from Class B, Class B inherits from Class A
- Hierarchical Inheritance: In this type of inheritance, two or more classes inherit from one class. For e.g. Class D inherits from Class A, Class E inherits from Class A
Note. Multiple inheritance (One class inheriting from two or more classes) was supported in C++, but it's not supported in advance OOP languages like C# and Java.
Composition is another way to support code reusability. Here, as the name suggested, one class is composed of another class. For example, let's say class B needs some capabilities of class A. In that case, instead of inheriting from class A, it will have a reference of class A added to class B.
This can be achieved by declaring a class A variable inside class B, initializing it in the constructor, and only using needed properties and functions inside it.
Output
I have explained the concept of inheritance and composition in simple words with examples. Now, let’s discuss the pros and cons of using them over each other.
Evaluate your needs and always prefer using composition over inheritance because of the drawbacks inheritance can introduce in the long term in your enterprise application.
-
Consider a scenario where you created one base class, and that base class is inherited by many classes in your application across domain boundaries. In that case, if that base class changes because of any reason, then it can potentially break other derived classes.
For example, The base class function changes its return type or method signature, which requires a fix in all derived classes.
-
When one class inherits another class, it gets everything of the base class irrespective of what is required. It can lead to unnecessarily writing unit test cases for all the base class capabilities that are not in use in the derived class.
-
In the case of composition, we can have a base class or base interface composed (or referred) inside another class, and then run time any class implementing that interface can be composed. This is also called run time polymorphism. This is not possible in the case of inheritance.
For example, Instead of having Animal as a base class and Cat, Dog, Tiger, or Human inheriting from it, it's better to create a small interface based on capabilities like IBarkable, IMeowable, ITalkable inheriting from ICommunicate and use ICommunicate in composition and runtime passes the needed reference of the concrete class implementing the relevant interface.
Use inheritance in case of a perfect is-a relationship. If you need all the functionalities in a base class and you just want to extend it a bit, and if it's a single inheritance, one base class, and one derived class for special purpose needs, then it's fine. If a derived class only needs a few things in the base class but not all the things, then rethink using inheritance. Instead of that, you can prefer taking that capability out as an interface and then using composition instead of inheritance.