D- What is Dependency Inversion?
The Dependency Inversion principle is essentially based on Open/Closed and Liskov Substitution Principle. If you are not familiar with these two principles, it is a good idea to take a look at OCP and LSP before reading this article.
The OC principle argues that pieces of software should be open to extension but closed to modification. In order to achieve this, when you want to add functions to your module, instead of writing these functions directly to the module, you need to write these functions in different interfaces and implement these interfaces in your modules. Thus, instead of changing the module content to give your module a new functionality, we create a new interface and load the functionality into this interface and implement this interface to our module.
The LS principle argues that you should be able to replace other modules of the same interface without causing any errors.
The Dependency Inversion principle has two rules for managing dependency between modules.
1- High-level modules should not be dependent on low-level modules. Both should be dependent on abstractions (abstract class/interface).
2- Abstractions should not depend on details. Details should depend on abstractions.
What is abstraction?
Abstraction meaning is loading functionalities on another structure for reducing the complexity of the software and using this abstracted structure when we need these functionalities.
We can achieve abstraction with abstract classes and interfaces. As we mentioned earlier, abstractions can be made through classes and tightly bind the two modules. In addition, abstractions made with the interface loosely connect these two modules.
What is a high-low level module?
A module that depends on other modules to perform its functionalities is called a high-level module. The modules that this structure depends on are low-level modules.
How can we understand that a module is dependent on other modules?
As we learned in the previous SOLID articles, the module that has the new keyword in it, is tightly coupled to the module which was created an instance with the new keyword.
Now let’s do a basic example of DIP.
This module is a high-level module, as we have implemented our other modules in our Feed module above. Since we created an instance with the new keyword in this high-level module, our module becomes tightly coupled to other submodules.
Now let’s change this tight coupling into the loose coupling.
Since we do not create any instances from other modules with the new keyword in our feed module, this module is not tightly coupled to modules derived from IEat, but loosely.
Dependency Inversion VS Dependency Injection
Dependency Inversion and Dependency Injection are good separate structures that are often confused with each other. As we mentioned at the beginning of our article, dependency inversion is a software principle. It tells us how to manage our dependencies.
Dependency Injection is a technique, design pattern used to manage our dependencies. To mention briefly;
Dependency Injection can be applied in 3 different ways.
Constructor Injection: We transmit and inject our modules to our class through the constructor.
Property(Setter) Injection: We transmit and inject our modules to our class via a public property.
Method(Interface) Injection: Our interfaces to be injected are injected into our class via the specially written method.
Since our article’s topic is DIP, I will not explain more about Dependency Injection. You can find Dependency Injection and other design patterns in the ‘design patterns’ series in a short time.