Dependence Inversion Principle DIP

High-level modules should not depend on low-level modules, they should all depend on abstractions; Abstraction should not depend on details, details should depend on abstractions.

A simple definition is: program interface oriented (abstract), not implementation oriented.

What are high-level modules? Simply put, the level of encapsulation is high and we consider it a high-level module. UnlockPhone UnlockPhone UnlockPhone UnlockPhone UnlockPhone UnlockPhone UnlockPhone UnlockPhone

What are the details? The detail is the instance method, which is a complete, small enough unit of logic, a subroutine that contains code. What is abstraction? In c #, the abstract is the abstract class (accurately, should is the abstract method in an abstract class, because in an abstract class can have instance methods) or interface, they can’t be instantiated directly, only through a subclass of the abstract class, interface, the implementation of the class or the factory method provides the instance (container can also provide examples, but its essence is still the factory). In fact, abstraction can’t depend on details at all, because C# syntax dictates that abstract methods and interfaces can’t contain implementation, that is, they can’t contain details, which is “abstraction shouldn’t depend on details”. So what does “detail should depend on abstraction” mean? The details should depend on abstraction. This can be thought of as an updated version of The Richter’s substitution principle, which requires the use of abstract base classes or interfaces as arguments to methods whenever possible.

The sample

public class XiaoMiPhone {

    public bool Unlock()= >true;

}
Copy the code
public class Customer {

    public bool UnlockPhone(XiaoMiPhone phone) => phone.Unlock();

}
Copy the code
var customer = new Customer();
var phone = new XiaoMiPhone();

var lockResult = customer.UnlockPhone(phone);
Copy the code

The UnlockPhone method needs a parameter from XiaoMiPhone. The UnlockPhone method needs a parameter from XiaoMiPhone. The UnlockPhone method needs a parameter from XiaoMiPhone. No matter we modify the Customer class or XiaoMiPhone class, we cannot guarantee that the caller can run correctly. We need to do a complete regression test for these two classes. Another problem is that one day when we want to unlock the IphoneX, we will have to make massive changes to the code above, which clearly violates the open and close principle. The following is a solution for your reference:

public interface IMobilePhone {

    bool Unlock();

}
Copy the code
public class XiaoMiPhone : IMobilePhone {

    public bool Unlock() {
        Console.WriteLine("Use fingerprint to unlock your phone!");
        return true; }}Copy the code
public class ApplePhoneX : IMobilePhone {

    public bool Unlock() {
        Console.WriteLine("Use Face ID to unlock your phone!");
        return true; }}Copy the code
public class Customer {

    public bool UnlockPhone(IMobilePhone phone) => phone.Unlock();

}
Copy the code
var customer = new Customer();

IMobilePhone phone = new XiaoMiPhone();
var lockResult = customer.UnlockPhone(phone);

phone = new ApplePhoneX();
lockResult = customer.UnlockPhone(phone);
Copy the code

Firstly, the contract is established through IMobilePhone and Unlock method is provided. XiaoMiPhone and ApplePhoneX class realize IMobilePhone interface. The high-level module Customer no longer depends on a certain mobile phone class, but depends on IMobilePhone interface. That is, high-level modules rely on abstraction. What about low-level modules? The low-level module in this example is a specific mobile phone class, which does not depend on any modules. The concepts of high and low level modules are relative. In the actual development process, the low-level module ApplePhoneX may rely on other lower level modules to provide more functions. After all, “there is life, there is dependence.”

Through the above analysis, it is not difficult to find that the original high-level modules depend on low-level modules, but after code transformation, they all depend on abstraction, that is, dependency has been transferred, which is the so-called “dependency inversion principle”. Dependency Injection inversion is known as Dependency Injection. There are three common Dependency Injection modes: structure Injection, set value Injection and interface Injection.

Note: There is another way to inject a service locator, which will be covered in more detail in a future Asp.Net article.

Structural injection:

public class Customer {
    private IMobilePhone _phone = null;
    public Customer(IMobilePhone phone) { _phone = phone; }
    public bool UnlockPhone() => _phone.Unlock();
}
Copy the code

Set value injection:

public class Customer {

    public IMobilePhone Phone { get; set; }
    public bool UnlockPhone() => Phone.Unlock();

}
Copy the code

Interface injection:

interface IPhoneProvider {

    IMobilePhone Phone { get; set; }}Copy the code
public interface IMobilePhone {

    bool Unlock();

}
Copy the code
public class Custome : IPhoneProvider {

    public IMobilePhone Phone { get; set; }
    public bool UnlockPhone() => Phone.Unlock();

}
Copy the code

To sum up, we can easily come to the conclusion that injection is the means and dependency inversion is the end.