The Dependency Inversion Principle (DIP) is the final principle in the SOLID design principles. It states:
“High-level modules should not depend on low-level modules. Both should depend on abstractions.”
“Abstractions should not depend on details; details should depend on abstractions.”
These statements emphasize that software design should prioritize flexibility and decoupling by ensuring that high-level policies are not directly tied to low-level implementation details. Both high-level and low-level modules should rely on abstractions (e.g., interfaces or abstract classes) to establish a flexible relationship. This approach keeps the system modular and adaptable.
Key Concepts of DIP
- High-Level Modules: These are components that contain the core business logic of the application. They should dictate the flow of the application without being dependent on specific implementation details.
- Low-Level Modules: These modules handle specific, detailed tasks, such as database operations or network communication. They should not directly control the application’s main flow.
- Abstractions: Interfaces or abstract classes that define general behaviors. High-level and low-level modules depend on these abstractions to communicate, keeping the system loosely coupled.
Understanding the Principle with an Example
Let’s say you’re developing a notification system where an EmailService sends notifications. Without DIP, the high-level module (e.g., NotificationService) would depend directly on the low-level EmailService. This makes it difficult to switch to other notification types, such as SMS or push notifications, without modifying the NotificationService.
Image scaled to 90%
In this design, the NotificationService is tightly coupled to EmailService, violating DIP. If we want to use a different service for notifications, we would need to modify the NotificationService class directly.
Why This Violates DIP
- Tight Coupling: The high-level module (
NotificationService) is directly tied to a specific low-level implementation (EmailService). Changes in the low-level module require changes in the high-level module. - Lack of Flexibility: Switching to another notification type (e.g., SMS or push notifications) is not possible without modifying the existing code.
In this lesson, we’ve introduced the basics of DIP and demonstrated a common violation. In the next lesson, we’ll explore how to refactor the code to follow DIP by introducing abstractions.