DIP - The Dependency Inversion Principle
The DIP (Dependency Inversion Principle) is the last of the 5 Principles of the SOLID acronym. This doesn't make this principle less or more important than the others, but rather as important as them.
Definition of DIP
Again, I haven't brought any impactful phrase written (or said) by some star of software development and/or architecture, but we can define and state that:
To reduce coupling, a class/type should not depend directly on concrete implementations.
Abstractions (interfaces or abstract classes) work as contracts. This means that it doesn't matter how the implementation was done or which library is being used, as long as the injected class/type is capable of meeting the established contract.
When we talk about concrete classes/types, we can ignore those that are considered non-volatile, that is, those that are stable and unlikely to undergo changes at any time due to technical or business needs.
An example of a concrete class considered non-volatile is StringBuilder. Although it implements the ISerializable interface, it is a generic, stable class and unlikely to undergo drastic changes in its implementation. Furthermore, it has no external dependencies that could cause any kind of instability.
Now, as usual, let's proceed with our examples of violation and application. If you haven't understood, don't worry and I guarantee that you will leave this article fully enlightened.
Violation of DIP
Let's observe the following code:
public class EmailService
{
public void Send(string destination, string message)
{
// Logic to send Email.
}
}
public class Notifier
{
private readonly EmailService _emailService;
public Notifier(EmailService emailService)
{
_emailService = emailService;
}
public void Notify(string destination, string message)
{
_emailService.Send(destination, message);
}
}
We can notice that the Notifier class has a direct dependency on the EmailService class. Whenever EmailService is changed or a new notification service is implemented, the Notifier class will be forced to be modified and recompiled because it is strongly coupled to the EmailService class implementation.
As we've already discussed in other articles, one of our main objectives is to avoid high coupling in code. A strongly coupled system becomes difficult to maintain, increasing the chance of introducing errors when changing something that is already validated and published in the production environment, also violating the OCP (Open/Closed Principle).
Application of DIP
Now, let's refactor this code to follow the Dependency Inversion Principle (DIP) and observe the benefits that this approach brings to software design:
public interface INotificationService
{
void Send(string destination, string message);
}
public class EmailService : INotificationService
{
public void Send(string destination, string message)
{
// Logic to send Email.
}
}
public class Notifier
{
private readonly INotificationService _notificationService;
public Notifier(INotificationService notificationService)
{
_notificationService = notificationService;
}
public void Notify(string destination, string message)
{
_notificationService.Send(destination, message);
}
}
After refactoring, the Notifier class now depends on an interface called INotificationService. It doesn't matter if the injected implementation will send notifications via email, SMS, or even smoke signals. What matters is that the implementation meets the contract defined by the interface, implementing the Send method, which receives the parameters referring to the destination and the message that should be sent.
The idea is straightforward.
Adopting SOLID principles is a good practice that promotes the creation of more flexible, scalable, and easily maintainable code. Of course, you're not obligated to follow them, but, in my opinion, in the long run, the benefits of applying them far outweigh the initial challenges.
From here, evaluate opportunities to apply the principle in your code.
Thanks for reading. To deepen: Clean Architecture — A Craftsman's Guide to Software Structure and Design (Robert C. Martin, 2019).