Dependency Inversion Principle Explained with Java Example
Learn the Dependency Inversion Principle with a simple Java example. Discover how using abstractions makes your code flexible and testable.
Dependency Inversion Principle (DIP)

The D in the SOLID principles stands for the Dependency Inversion Principle. By the end of this chapter, you'll be able to confidently answer:
-
What is the Dependency Inversion Principle?
-
Why do we need it? (What problems arise if we don’t follow it?)
-
How do we apply it in real-world code?
What is Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules. Instead, both should depend on abstractions (like interfaces or abstract classes). This helps decouple the components of the system.
If you're new to terms like high-level modules and low-level modules, don’t worry! Let’s break it down with an example.
Without Dependency Inversion (Bad Approach)
Let's first look at what happens if we don't follow DIP.

In this diagram, we see that the service layer is the high-level module (which handles complex logic), while the repository layer is the low-level module (which performs tasks like saving, updating, or retrieving data from a database).
Currently, the service layer is tightly coupled with the repository layer. Let’s see this with some code:
In this case, the UserService class is tightly coupled to the SQLRepository. If we decide to switch from SQL to MongoDB, we would need to modify the UserService class, making the code less flexible and harder to maintain.
With Dependency Inversion (Good Approach)
To follow the Dependency Inversion Principle, we introduce an abstraction (an interface), IRepository, which is then implemented by the repositories (SQLRepository, MongoRepository, etc.). This way, UserService depends on the abstraction, not on a specific repository implementation.

Here’s how we can refactor the code:
Now, the UserService is no longer dependent on a specific repository class. It can work with any class that implements the IRepository interface, whether it's the SQLRepository, MongoRepository, or FakeRepository.
Here’s the Main class that shows how we can use different repositories without modifying UserService:
Benefits of Dependency Inversion
-
Decoupling: By depending on abstractions (interfaces or abstract classes) instead of concrete implementations, we reduce the dependencies between components. This makes the code easier to modify and extend.
-
Flexibility: It's much easier to switch from one implementation to another (for example, switching from SQL to MongoDB) without affecting other parts of the system.
-
Maintainability: Since changes in low-level modules (like repositories) don’t affect high-level modules (like the service layer), the code becomes easier to maintain and scale.