Dive into the popular design pattern
This post is part 5 of a series on the SOLID principles.
You can find post 4 here, post 3 here, post 2 here and post 1 here.
We have finally reached the last of the SOLID principles 😇
As usual we start with a definition:
Principle 5 is named the dependency inversion principle.
The definition has two parts:
A. High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).
B. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.
Source: Dependency inversion principle - Wikipedia
In traditional software architecture we design lower level components to be used/consumed by higher level components. In other words, higher level components depend on lower level components. This dependency causes tight coupling in the software. As explained in principle one, we strive to achieve loose coupling to make it easier to develop, maintain and change code in the future.
The dependency inversion principle inverts this dependency in the sense that instead of higher level components depending on lower level ones, both should depend on abstractions. This abstraction layer would be an intermediate component that sits between the higher and lower level components. The two will then use this component to communicate and interact amongst each other. The abstraction component would usually be implemented as an interface.
Time for some code and a concrete example!
Consider the following example where we model a robot:
Robot class has only one function
We model an
Apple that the robot can get energy from.
Now, at some point the robot will get tired of eating apples.
So we add a
Chocolate class to offer more eating options to the robot.
Notice however, what happens to the
get_energy method. We must pass a string as a parameter indicating what eatable the robot should eat. Also, we must use if-else branching on the two different eatables. Now you can imagine if more eatables are added we would need more else branches. This is not good design.
Robot has a dependency on each of the individual eatables we will run into issues if the implementation of one of the eatables changes. For example, if we introduce an additional parameter to the
eat method in
Chocolate the code in
get_energy would break and we would have to refactor it to reflect the changes in
Chocolate. These issues occur due to tight coupling and strong dependencies and are a violation of the dependency inversion principle.
Currently our architecture looks as follows:
To solve this, we need to introduce an abstraction layer.
We modify the architecture as follows:
The code looks like so:
We create an
Eatable interface that is implemented by both
We change the method signature of
get_energy so that it expects an argument of type
Eatable instead of
str. This means we can get rid of the if-else branching. Furthermore, since all eatables implement the
Eatable interface we are sure that there will be no code breakage if there changes to
In this post, we looked at the dependency inversion principle. The principle essentially states that higher level modules should not depend on lower level modules. Instead both should depend on abstractions. We make higher level modules independent of implementation specifics in lower level modules.
- Enforce loose coupling and thereby helps make code more robust in face of changes.
- Allows re-use of higher level components since the abstraction layer prevents code breakage in case the lower level components need to be changed.
We have finally reached the end!
I hope you enjoyed this post and all the previous posts on the SOLID principles.
You might consider signing up for my newsletter to get awesome content in the future!
Enjoyed this article? Please consider subscribing using the form below😇
It is 100% free. You get an email every time I post (about once a week) + access to premium content in the future. Connect with me on LinkedIn or follow on Twitter.