Home > Java > javaTutorial > body text

Dependency Inversion Principle

王林
Release: 2024-08-26 06:32:31
Original
661 people have browsed it

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.

Let's understand the High-level modules and Low-level modules through an example:

Dependency Inversion Principle

In an e-commerce app like Flipkart on a high level it can be categorized as ProductCatalog, PaymentProcessor, and CustomerProfile (these are some of the main business functions)
These business functions interdepend on other modules shown in the above image.

Note: the modules on top are closer to a business function called High level modules.
The modules at the bottom are close to the implementation details called Low level modules.

Low-level modules are SQLProductRepository, GooglePayService, WireTransfer, EmailSender, and VoiceDialer.

If we consider CustomerProfile (High-Level module) and Communication modules alone then, Communication is a Low-level module but if we consider Communication, EmailSender, and VoiceDialer alone then, Communication becomes a High-Level module, and EmailSender and VoiceDialer are Low-Level modules.

The point here the concept of High and Low level module is not absolute but relative.

According to the image above ProductCatalog depends on SQLProductRepository i.e. a high-level module depends on a low-level module, but this directly conflicts with the DIP's 1st definition.


Let's take ProductCatalog → SQLProductRepository relationship and analyze it further.

import java.util.List;
/*
 * High-Level module
*/
public class ProductCatalog {
    public void listAllProducts(){
        SQLProductRepository sqlProductRepository = new SQLProductRepository();
        List<String> allProductsNames = sqlProductRepository.getAllProductNames();
        //Display all products names
    }
}
Copy after login
/*
 * Low-level module 
*/
import java.util.Arrays;
import java.util.List;
public class SQLProductRepository {
    public List<String> getAllProductNames(){
        return Arrays.asList("soap","toothpaste");
    }
}
Copy after login

As ProductCatalog directly depends on SQLProductRepository this is clearly a violation of DIP definition 1 (as per the definition both High and Low-level modules should depend on abstraction)

Let's fix this as per definition 1:

creating interface ProductRepository

import java.util.List;

public interface ProductRepository {
    public List<String> getAllProductNames();
}
Copy after login

Implementing this interface in SQLProductRepository

/*
 * Low-level module 
*/
import java.util.Arrays;
import java.util.List;
public class SQLProductRepository  implements ProductRepository{
    @Override
    public List<String> getAllProductNames(){
        return Arrays.asList("soap","toothpaste");
    }
}
Copy after login

Finally for High level module ProductCatalog we should not directly instantiate SQLProductRepository in it. We will use a ProductFactory class for the same

public class ProductFactory {
    public static ProductRepository create(){
        return new SQLProductRepository();
    }
}
Copy after login

We will use ProductFactory to instantiate the SQLProductRepository

/*
 * High-Level module
*/
import java.util.List;

public class ProductCatalog {
    public void listAllProducts(){
        ProductRepository productRepository = ProductFactory.create();
        List<String> allProductsNames = productRepository.getAllProductNames();
        //Display all products names
    }
}
Copy after login

Note our reference object is ProductRepository So, we don't have any tight coupling with SQLProductRepository

After the modification the new dependency will look something like this

Dependency Inversion Principle

The above changes are as per the DIP definition 1.
The above code change also follows the 2nd definition of DIP as well i.e. Abstraction should not depend on the details, details should depend on the abstraction.
As we can see in the image above SQLProductRepository depends on the ProductRepository not the other way around. This is the reason why this principle is called the Dependency Inversion Principle


Dependency Injection VS Dependency Inversion

Even though they are related, they are not the same and can not be used interchangeably 
Copy after login

Understanding Dependency Injection:

In ProductCatalog we make use of the Factory method ProductFactory.create() to get an instance of SQLProductRepository object.
Though it delegates the instance creation process to the factory class ProductFactory, the initialization process is still with the ProductCatalog class.
Ideally, we don't want ProductCatelog class to worry about how and when to trigger the instantiation.
What if we provide the instantiated ProductRepository class to ProductCatalog even without it asking?

So, the Main class ECommerceMainApplication makes use of the factory method ProductFactory.create() to create the instance of ProductRepository and this instance is passed as an argument in the constructor of ProductRepositroy class.

public class ECommerceMainApplication {
    public static void main(String agrs[]) {
        ProductRepository productRepository = ProductFactory.create();
        ProductCatalog productCatalog = new ProductCatalog(productRepository);
        productCatalog.listAllProducts();
    }
}
Copy after login

After updating the ProductCatalog class accordingly

import java.util.List;

public class ProductCatalog {

    private ProductRepository productRepository;

    public ProductCatalog(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    public void listAllProducts(){
        List<String> allProductsNames = productRepository.getAllProductNames();
        //Display all products names
        allProductsNames.forEach(product-> System.out.println(product));
    }
}
Copy after login

Now the ProductCatalog is free to use the SQLProductRepository object whenever and wherever it wants. It no longer has not worry about creating the SQLProductRepository object on its own.
In other words we are injecting the dependency into the ProductCatalog instead of ProductCatalog worrying about instantiating the dependency.
This is the concept of dependency injection


Inversion of control - IOC

Even though it is not part of DIP(Dependency Inversion Principle), it is closely related

Let us understand this with the same above code

The class ProductCatalog had a constructor that took in ProductRepository object.

The class that calls the ProductCatalog will provide or inject the object of ProductRepository in this case it is ECommerceMainApplication.
Note, even though the injection happens outside of the ProductCatalog class, the injection still happens during the main flow of the program. i.e. the injection is happening in the main thread of the program execution.

What if we want all the injections to happen in a separate thread or a separate context altogether So that the main control flow is completely isolated from the injection?

This can be achieved using frameworks like Spring(in Java).

Dependency Inversion Principle

Spring will run its own context different from the main flow of the program
Spring will take care of injecting the required dependencies of a class. So if you want to instantiate the object of a class, instead of doing it yourself directly in the code, you ask Spring to give you the object of the class.
The Spring framework looks at all the dependencies required for the instantiation of the object, then goes ahead and injects all the dependencies, instantiates the object, and gives it back to the main control flow.
Thus the control over dependency injection is completely delegated to the Spring framework and does not happen in the mail control flow.
This concept is called Inversion of Control (IOC) and the Spring is called Inversion of Control Container or simply an IOC Container

The above is the detailed content of Dependency Inversion Principle. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!