Dependency Inversion Principle (DIP)
The Dependency Inversion Principle (DIP) is one of the five object-oriented design principles that form SOLID. It emphasizes that high-level modules should not depend on low-level modules; both should depend on abstractions.
The DIP states that the inversion of control should be applied to achieve loose coupling and improved flexibility. Instead of high-level modules depending on low-level modules directly, they should both depend on abstractions, typically defined by interfaces or abstract classes.
To understand the need for the DIP, let's consider an example where we have a UserController
class, which depends on a UserRepository
class to retrieve user data from a database:
1public class UserController {
2 private UserRepository userRepository;
3
4 public UserController() {
5 this.userRepository = new UserRepository();
6 }
7
8 public void getUser(int userId) {
9 User user = userRepository.getUser(userId);
10 // process user data
11 }
12}
13
14public class UserRepository {
15 public User getUser(int userId) {
16 // retrieve user data from database
17 return user;
18 }
19}
In this example, the UserController
directly depends on the UserRepository
class, creating a strong coupling between the two. This makes the UserController
less flexible and harder to test, as it becomes tightly coupled to the implementation details of the UserRepository
.
By applying the DIP, we can achieve loose coupling and improved flexibility. Instead of the UserController
depending directly on the UserRepository
, we can introduce an abstraction, such as an IUserRepository
interface, that both the UserController
and UserRepository
depend on:
1public interface IUserRepository {
2 User getUser(int userId);
3}
4
5public class UserController {
6 private IUserRepository userRepository;
7
8 public UserController(IUserRepository userRepository) {
9 this.userRepository = userRepository;
10 }
11
12 public void getUser(int userId) {
13 User user = userRepository.getUser(userId);
14 // process user data
15 }
16}
17
18public class UserRepository implements IUserRepository {
19 public User getUser(int userId) {
20 // retrieve user data from database
21 return user;
22 }
23}
In this refactored example, the UserController
now depends on the IUserRepository
interface instead of the UserRepository
class directly. This allows for better separation of concerns and flexibility, as we can easily swap out different implementations of the IUserRepository
interface without affecting the UserController
.
By following the DIP, we can achieve loose coupling, inversion of dependencies, and improved flexibility in our codebase. This leads to code that is easier to understand, maintain, and test.
xxxxxxxxxx
class Main {
public static void main(String[] args) {
// replace with your Java logic here
}
}