The NET IoC mode depends on inversion of DIP control inversion of Ioc dependency injection of DI

  • 2021-11-13 07:16:20
  • OfStack

Dependence inversion principle (DIP)

Dependency inversion (Dependency Inversion Principle, abbreviation DIP) is one of the six basic principles of object-oriented. It refers to a specific decoupling form, which makes the high-level module independent of the implementation details of the low-level module, and the dependency relationship is reversed (reversed), so that the low-level module depends on the requirement abstraction of the high-level module.

This principle stipulates:

High-level modules should not rely on low-level modules, and both should rely on their abstract interfaces. Abstract interfaces should not depend on concrete implementations, but concrete implementations should depend on abstract interfaces.

Let's take a look at a simple example below, where we send a relevant text message or email to our users through a simple ordering process.


public SendingEmail
{
 public void Send(string message){
  //do something
 }
}

public Ordering
{
 SendingEmail _sendingEmail=null;
 public void Order(string message){
  //Order business operation
  if(_sendingEmail == null)
  {
   _sendingEmail=new SendingEmail();
  }
  _sendingEmail.Send(message);
 }
}

It's okay to look at our code in this way. At present, as long as we complete the order operation, Then it will trigger the sending function, but it violates DIP, because Ordering class depends on SendingEmail class, and SendingEmail class is not an abstract class, but a concrete class. Then let's think about a new one. If people from the business port come to us at this time and put forward a new requirement, asking us to change to SMS instead of Email, what should we do?


public class SendingSMS
{
 public void Send(string message){
  //do something
 }
}
public Ordering
{
 SendingEmail _sendingEmail=null;
 SendingSMS _sendingSMS=null;
 bool isSendingSMS=true;
 public void Order(string message){
  //Order business operation
  if(isSendingSMS){
   if(_sendingSMS == null)
   {
    _sendingSMS=new SendingSMS();
   }
   _sendingSMS.Send(message);
  }else{
   if(_sendingEmail == null)
   {
    _sendingEmail=new SendingEmail();
   }
   _sendingEmail.Send(message);
  }
  
 }
}

According to the above requirements, we have to create more classes and declare them in the Ordering class. Finally, we need to use the IF ELSE statement to decide whether to use SMS or email. But when we have more such processing operations, it may be more confusing than it is now, which means that we must declare more instances of new concrete classes in the Ordering class.

We need to pull out a way for high-level modules to rely on abstraction instead of our implementation class, which will be mapped to the implementation class.

Inversion of Control (IoC)

Inversion of control (Inversion of Control, abbreviated as IOC) is an object-oriented design principle that can help us make high-level modules rely on abstraction rather than the concrete implementation of low-level modules. In other words, it helps to implement (rely on inversion principle-DIP).


public interface ICustomerCommunication
{
 void Send(string message);
}
 Then we modify SendingEmail And SendingSMS Class to derive from the ICustomerCommunication Interface inheritance .

public class SendingEmail:ICustomerCommunication
{
 public void Send(string message){
  //do something
 }
}

public class SendingSMS:ICustomerCommunication
{
 public void Send(string message){
  //do something
 }
}

Let's modify the Ordering class under 1 to use this abstract interface


public Ordering
{
 ICustomerCommunication _customerComm=null;
 bool isSendingSMS=true;
 public void Order(string message){
  //Order business operation
  if(isSendingSMS){
   if(_customerComm == null)
   {
    _customerComm=new SendingSMS();
   }
   _customerComm.Send(message);
  }else{
   if(_customerComm == null)
   {
    _customerComm=new SendingEmail();
   }
   _customerComm.Send(message);
  }
  
 }
}

The inversion of control we did with the above modifications is more consistent with DIP. Now our high-level modules only need to rely on abstraction, not implementation.

Dependency Injection (DI)

Dependency injection (Depeondency Injection, abbreviated as DI) is a way to implement inversion of control. There are three commonly used methods of dependency injection:

Constructor injection Method injection Attribute injection

Although we implemented IoC with the above code, and the Ordering class depends on the ICustomerCommunication abstraction, we still use the implementation class in the Ordering class, which makes it impossible for us to completely decouple from class to class.


 if(isSendingSMS){
 if(_customerComm == null)
 {
  _customerComm=new SendingSMS();
 }
  _customerComm.Send(message);
 }else{
  if(_customerComm == null)
  {
   _customerComm=new SendingEmail();
  }
  _customerComm.Send(message);
 }

Let's talk about DI again. DI mainly helps us inject implementation into abstract classes (ICustomerCommunication interface). DI mainly reduces the coupling between classes and removes the binding between abstract and concrete implementation from dependent classes.

Constructor injection

Through constructor injection we pass the object that implements the class to the constructor of the dependent class and assign it to the interface.


public class Ordering
{
 ICustomerCommunication _customerComm=null;
 public Ordering(ICustomerCommunication customerComm){
  _customerComm=customerComm;
 }
 public void Order(string message){
  _customerComm.Send(message);
 }
}

In the above code, the constructor will bind to the interface with the implementation class object. If we pass the implementation of SendingSMS to this class, all we have to do is declare an instance of the SendingSMS class and pass it to the constructor of Ordering as follows:

Method injection

By using constructor injection, we will have to use an instance of the implementation class SendingSMS or SendingEmail for the lifetime of the Ordering class. Now if you want to pass an instance of the implementation class every time the method is called, you must use method injection.


public class Ordering
{
 public void Order(ICustomerCommunication customerComm,string message){
  _customerComm=customerComm;
  _customerComm.Send(message);
 }
}

The invocation mode is as follows


SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.Order(sendingSMS,"msg");

Attribute injection

From the above description, we know that the constructor injection method uses dependent classes in the whole life cycle, while the method injection is to limit our injection directly to the method, and then we will understand the attribute injection under 1


public class Ordering
{
 public ICustomerCommunication customerComm {get;set;}
 public void Order(string message){
  _customerComm.Send(message);
 }
}

The invocation mode is as follows


SendingSMS sendingSMS=new SendingSMS();
Ordering ordering=new Ordering(sendingSMS);
ordering.customerComm=sendingSMS;
ordering.Order("msg");

Constructor injection is the most common way to implement DI. If you need to pass different dependencies on each method call, you can use method injection. Attribute injection is used less often.

Reference

https://zh. wikipedia. org/wiki/Reversal of Control

https://zh.wikipedia.org/zh-hans/Dependency Inversion Principle


Related articles: