What is the dependency inversion principle and why is it important?

前端 未结 15 976
别那么骄傲
别那么骄傲 2020-11-28 00:06

What is the dependency inversion principle and why is it important?

相关标签:
15条回答
  • 2020-11-28 00:52

    Inversion of control (IoC) is a design pattern where an object gets handed its dependency by an outside framework, rather than asking a framework for its dependency.

    Pseudocode example using traditional lookup:

    class Service {
        Database database;
        init() {
            database = FrameworkSingleton.getService("database");
        }
    }
    

    Similar code using IoC:

    class Service {
        Database database;
        init(database) {
            this.database = database;
        }
    }
    

    The benefits of IoC are:

    • You have no dependency on a central framework, so this can be changed if desired.
    • Since objects are created by injection, preferably using interfaces, it's easy to create unit tests that replace dependencies with mock versions.
    • Decoupling off code.
    0 讨论(0)
  • 2020-11-28 00:52

    The point of dependency inversion is to make reusable software.

    The idea is that instead of two pieces of code relying on each other, they rely on some abstracted interface. Then you can reuse either piece without the other.

    The way this is most commonly achieved is through an inversion of control (IoC) container like Spring in Java. In this model, properties of objects are set up through an XML configuration instead of the objects going out and finding their dependency.

    Imagine this pseudocode...

    public class MyClass
    {
      public Service myService = ServiceLocator.service;
    }
    

    MyClass directly depends on both the Service class and the ServiceLocator class. It needs both of those if you want to use it in another application. Now imagine this...

    public class MyClass
    {
      public IService myService;
    }
    

    Now, MyClass relies on a single interface, the IService interface. We'd let the IoC container actually set the value of that variable.

    So now, MyClass can easily be reused in other projects, without bringing the dependency of those other two classes along with it.

    Even better, you don't have to drag the dependencies of MyService, and the dependencies of those dependencies, and the... well, you get the idea.

    0 讨论(0)
  • 2020-11-28 00:52

    Dependency Inversion Principle (DIP) says that

    i) High level modules should not depend upon low-level modules. Both should depend upon abstractions.

    ii) Abstractions should never depend upon details. Details should depend upon abstractions.

    Example:

        public interface ICustomer
        {
            string GetCustomerNameById(int id);
        }
    
        public class Customer : ICustomer
        {
            //ctor
            public Customer(){}
    
            public string GetCustomerNameById(int id)
            {
                return "Dummy Customer Name";
            }
        }
    
        public class CustomerFactory
        {
            public static ICustomer GetCustomerData()
            {
                return new Customer();
            }
        }
    
        public class CustomerBLL
        {
            ICustomer _customer;
            public CustomerBLL()
            {
                _customer = CustomerFactory.GetCustomerData();
            }
    
            public string GetCustomerNameById(int id)
            {
                return _customer.GetCustomerNameById(id);
            }
        }
    
        public class Program
        {
            static void Main()
            {
                CustomerBLL customerBLL = new CustomerBLL();
                int customerId = 25;
                string customerName = customerBLL.GetCustomerNameById(customerId);
    
    
                Console.WriteLine(customerName);
                Console.ReadKey();
            }
        }
    

    Note: Class should depend on abstractions like interface or abstract classes, not specific details (implementation of interface).

    0 讨论(0)
  • 2020-11-28 00:56

    Dependency inversion well applied gives flexibility and stability at the level of the entire architecture of your application. It will allow your application to evolve more securely and stable.

    Traditional layered architecture

    Traditionally a layered architecture UI depended on the business layer and this in turn depended on the data access layer.

    You have to understand layer, package, or library. Let's see how the code would be.

    We would have a library or package for the data access layer.

    // DataAccessLayer.dll
    public class ProductDAO {
    
    }
    

    And another library or package layer business logic that depends on the data access layer.

    // BusinessLogicLayer.dll
    using DataAccessLayer;
    public class ProductBO { 
        private ProductDAO productDAO;
    }
    

    Layered architecture with dependency inversion

    The dependency inversion indicates the following:

    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.

    What are the high-level modules and low level? Thinking modules such as libraries or packages, high-level module would be those that traditionally have dependencies and low level on which they depend.

    In other words, module high level would be where the action is invoked and low level where the action is performed.

    A reasonable conclusion to draw from this principle is that there should be no dependence between concretions, but there must be a dependence on an abstraction. But according to the approach we take we can be misapplying investment depend dependency, but an abstraction.

    Imagine that we adapt our code as follows:

    We would have a library or package for the data access layer which define the abstraction.

    // DataAccessLayer.dll
    public interface IProductDAO
    public class ProductDAO : IProductDAO{
    
    }
    

    And another library or package layer business logic that depends on the data access layer.

    // BusinessLogicLayer.dll
    using DataAccessLayer;
    public class ProductBO { 
        private IProductDAO productDAO;
    }
    

    Although we are depending on an abstraction dependency between business and data access remains the same.

    To get dependency inversion, the persistence interface must be defined in the module or package where this high level logic or domain is and not in the low-level module.

    First define what the domain layer is and the abstraction of its communication is defined persistence.

    // Domain.dll
    public interface IProductRepository;
    
    using DataAccessLayer;
    public class ProductBO { 
        private IProductRepository productRepository;
    }
    

    After the persistence layer depends on the domain, getting to invert now if a dependency is defined.

    // Persistence.dll
    public class ProductDAO : IProductRepository{
    
    }
    


    (source: xurxodev.com)

    Deepening the principle

    It is important to assimilate the concept well, deepening the purpose and benefits. If we stay in mechanically and learn the typical case repository, we will not be able to identify where we can apply the principle of dependence.

    But why do we invert a dependency? What is the main objective beyond specific examples?

    Such commonly allows the most stable things, that are not dependent on less stable things, to change more frequently.

    It is easier for the persistence type to be changed, either the database or technology to access the same database than the domain logic or actions designed to communicate with persistence. Because of this, the dependence is reversed because as it is easier to change the persistence if this change occurs. In this way we will not have to change the domain. The domain layer is the most stable of all, which is why it should not depend on anything.

    But there is not just this repository example. There are many scenarios where this principle applies and there are architectures based on this principle.

    Architectures

    There are architectures where dependency inversion is key to its definition. In all the domains it is the most important and it is abstractions that will indicate the communication protocol between the domain and the rest of the packages or libraries are defined.

    Clean Architecture

    In Clean architecture the domain is located in the center and if you look in the direction of the arrows indicating dependency, it is clear what are the most important and stable layers. The outer layers are considered unstable tools so avoid depending on them.


    (source: 8thlight.com)

    Hexagonal Architecture

    It happens the same way with the hexagonal architecture, where the domain is also located in the central part and ports are abstractions of communication from the domino outward. Here again it is evident that the domain is the most stable and traditional dependence is inverted.

    0 讨论(0)
  • 2020-11-28 00:57

    I can see good explanation has been given in above answers. However i wants to provide some easy explanation with simple example.

    Dependency Inversion Principle allows the programmer to remove the hardcoded dependencies so that the application becomes loosely coupled and extendable.

    How to achieve this : through abstraction

    Without dependency inversion:

     class Student {
        private Address address;
    
        public Student() {
            this.address = new Address();
        }
    }
    class Address{
        private String perminentAddress;
        private String currentAdrress;
    
        public Address() {
        }
    } 
    

    In above code snippet, address object is hard-coded. Instead if we can use dependency inversion and inject the address object by passing through constructor or setter method. Let's see.

    With dependency inversion:

    class Student{
        private Address address;
    
        public Student(Address address) {
            this.address = address;
        }
        //or
        public void setAddress(Address address) {
            this.address = address;
        }
    }
    
    0 讨论(0)
  • 2020-11-28 00:59

    Good answers and good examples are already given by others here.

    The reason DIP is important is because it ensures the OO-principle "loosely coupled design".

    The objects in your software should NOT get into a hierarchy where some objects are the top-level ones, dependent on low-level objects. Changes in low-level objects will then ripple-through to your top-level objects which makes the software very fragile for change.

    You want your 'top-level' objects to be very stable and not fragile for change, therefore you need to invert the dependencies.

    0 讨论(0)
提交回复
热议问题