On the understanding and thinking of IOC and di

Time:2021-5-1

1、 Preface

In the actual development process, we often encounter such a situation. When debugging and analyzing problems, we often need to record log information. At this time, we can use output to the console.

Therefore, we usually define a log class to implement the output log.

Define a logical processing method for generating validation,

public class Logger
    {
        public void AddLogger()
        {
            Console. Writeline ("log added successfully!");
        }
    }

Then output the results in the console.

static void Main(string[] args)
        {
            Logger logger = new Logger();
            logger.AddLogger();

            Console.Read();
        }

Looking at the results of the implementation, we think that we have completed the task, but this is just the beginning.

2、 Start

I believe you will encounter this situation in the development. Sometimes you need console output, but you may also need to output to text, database or remote server, etc. Therefore, the initial use of direct output to the console can not meet the conditions, so we need to rewrite the above code to achieve different output methods.

2.1 the first way

2.1.1 console mode

When using console output mode:

/// 
    ///Console output
    /// 
    public class ConsoleLogger
    {
       	public void AddLogger()
        {
            Console. Writeline ("console output: log added successfully!");
        }
    }

Define a processing logic class to get the output log, so we need to defineConsoleLoggerClass and initialize

/// 
    ///Define a unified class of output logs
    /// 
    public class LoggerServer
    {
         private readonly ConsoleLogger consoleLogger = new ConsoleLogger();// Add a private variable object and a private variable digital object 
        public void AddLogger()
        {
            consoleLogger.AddLogger();
        }
    }

Console output:

static void Main(string[] args)
    {
        LoggerServer loggerServer = new LoggerServer();
        loggerServer.AddLogger();

        Console.Read();
    }

Console output: log added successfully!

2.1.2 text output

When using text output log, we define a way to generate text log again

/// 
    ///Text output
    /// 
    public class FileLogger
    {
        public void AddLogger()
        {
            Console. Writeline ("text output: log added successfully!");
        }
    }

Then define a processing logic class to get validation again, so we need to defineImageVerificationClass and initialize

/// 
    ///Define a unified class of output logs
    /// 
    public class LoggerServer
    {
        private readonly FileLogger fileLogger = new FileLogger();// Add a private variable object 
        public void AddLogger()
        {
            fileLogger.AddLogger();
        }
    }

The final output results are as follows

Text output: log added successfully!

Through the above way, we have realized different ways to output different logs, but careful observation can find that this way is not a good way of software design.

So someone may change to the second way below, using the interface.

2.2 the second way

Define aILoggerInterface and declare aAddLoggermethod

public interface ILogger
    {
        void AddLogger();
    }

In console output modeConsoleLoggerClass, implementingILoggerInterface

/// 
    ///Console output
    /// 
    public class ConsoleLogger : ILogger
    {
        public void AddLogger()
        {
            Console. Writeline ("console output: log added successfully!");
        }
    }

In the text output log modeFileLoggerClass, implementingILoggerInterface

/// 
    ///Text output
    /// 
    public class FileLogger : ILogger
    {
        public void AddLogger()
        {
            Console. Writeline ("text output: log added successfully!");
        }
    }

Define a unified output log classLoggerServerclass

/// 
///Define a unified class for getting validation
/// 
public class VerificationServer
{
    private readonly ConsoleLogger consoleLogger = new ConsoleLogger();// Add a private variable object 
   //private readonly FileLogger fileLogger = new FileLogger();// Add a private variable object 
    public void AddLogger()
    {
       _logger.AddLogger();
    }
}

Finally, the console calls the output:

static void Main(string[] args)
    {
        LoggerServer loggerServer = new LoggerServer();
        loggerServer.AddLogger();

        Console.Read();
    }

Console output: log added successfully!

Although the second way uses the interface to reduce the coupling, it still does not achieve the desired effect, so the above two ways are not very good software design methods.

The poor scalability of the code and the high coupling between components violate the open close principle, which should be considered in the designOpen to extension and close to modification

2.3 thinking

Since you want to follow the open close principle, the above writing method, choose the console log output mode requiredConsoleLoggerCreation and dependency are in a unified log classLoggerServerInternally, since there is no direct binding dependency inside, is there any way to transfer it from the outsideLoggerServerHow to use class internal reference?

3、 Introduction

3.1 dependency injection

Dependency injection: it provides a mechanism to pass references to dependent objects.

Now let’s take a look at the specific injection methods, and then make a summary.

3.1.1 constructor injection

stayLoggerServerClass, define a private variable_loggerAnd then pass the dependency through the constructor

public class LoggerServer
{
    private ILogger _ logger; // 1. Define private variables
    //2. Constructor
    public LoggerServer(ILogger logger)
    {
        //3. Injection, transfer and dependence
    	this._logger = logger; 
    }

    public void AddLogger()
    {
   		 _logger.AddLogger();
    }
}

By calling the console program, the dependency object is created externally, and then the dependency is injected through construction

static void Main(string[] args)
        {
            #Region constructor injection
            //Injection console output mode
            //Create dependent objects externally - > console logger
            ConsoleLogger console = new ConsoleLogger();
            //Inject through constructor - > loggerserver
            LoggerServer loggerServer1 = new LoggerServer(console);
            loggerServer1.AddLogger();


            //Input file output mode
            FileLogger file = new FileLogger();
            //Inject through constructor - > loggerserver
            LoggerServer loggerServer2 = new LoggerServer(file);
            loggerServer2.AddLogger();
            
            #endregion

            Console.Read();
        }

Output:

Console output: log added successfully!

Text output: log added successfully!

Obviously, through this constructor injection method, we can define the dependency externally, reduce the internal coupling, and increase the scalability. We only need to modify the dependency externally to achieve different verification results.

3.1.2 attribute injection

That is, a dependency is passed by defining an attribute

/// 
    ///Define a unified class of output logs
    /// 
    public class LoggerServer
    {
        //1. Define an attribute to receive external assignment dependencies
        public ILogger _logger { get; set; }
        public void AddLogger()
        {
            _logger.AddLogger();
        }
    }

Through the console, different methods are defined, and different verification results are achieved through different dependency assignments

static void Main(string[] args)
        {
            #Region property injection
            //Injection console output mode

            //Create dependent objects externally - > console logger
            ConsoleLogger console = new ConsoleLogger();
            LoggerServer loggerServer1 = new LoggerServer();
            //Assign values to internal properties
            loggerServer1._logger = console;
            loggerServer1.AddLogger();

            //Input file output mode

            //Create dependent objects externally - > FileLogger
            FileLogger file = new FileLogger();
            LoggerServer loggerServer2 = new LoggerServer();
            //Assign values to internal properties
            loggerServer2._logger = file;
            loggerServer2.AddLogger();

            #endregion

            Console.Read();
        }

output

Console output: log added successfully!

Text output: log added successfully!

3.1.3 interface injection

First, define an interface, including a method to set dependency.

public interface IDependent
    {
 		void SetDepend(ILogger logger);// Set dependencies
    }

This is not the same as the previous injection method, but through in the classInherit and implement this interface

public class VerificationServer : IDependent
    {
 		private ILogger _logger;
        //Inherit interface, implement dependency method and inject dependency
        public void SetDepend(ILogger logger)
        {
            _logger = logger;
        }
        public void AddLogger()
        {
            _logger.AddLogger();
        }
    }

Pass the dependency directly through the dependency method by calling

static void Main(string[] args)
        {
            #Region interface injection
            //Injection console output mode
            //Create dependent objects externally - > console logger
            ConsoleLogger console = new ConsoleLogger();
            LoggerServer loggerServer1 = new LoggerServer();
            //Assign a value to the interior and pass it through the interface
            loggerServer1.SetDepend(console);
            loggerServer1.AddLogger();

            //Input file output mode
            //Create dependent objects externally - > FileLogger
            FileLogger file = new FileLogger();
            LoggerServer loggerServer2 = new LoggerServer();
            //Assign a value to the interior and pass it through the interface
            loggerServer2.SetDepend(file);
            loggerServer2.AddLogger();

            #endregion

            Console.Read();
        }

output

Console output: log added successfully!

Text output: log added successfully!

3.1.4 summary

Di dependency injection

It provides a mechanism to pass references to dependent objectsThrough Di, we canLoggerServerClass is externalConsoleLoggerObject is passed to theLoggerServerClass object. External resources (including objects, resources, constant data) needed to inject an object

Dependency injection gives the creation of objects to external management, which solves the problem of tight coupling of code. It is a mechanism for loose coupling of code.
Loose coupling makes the code more flexible, better able to cope with changes in requirements, and convenient for unit testing.

3.2 IOC

Control reversal(inversion of control, abbreviated asIoC)In object-oriented programming, is aSoftware design pattern, teach us how to design better, more loosely coupled programs.

In the above example, we found that if the dependent object is created by the internal class during the process of obtaining the object, the code will be directly and highly coupled, and it is difficult to maintain. Therefore, in order to avoid this problem, we use the external to provide the dependent object. When the internal object class is created, we pass the reference of the dependent object to it, The dependency is injected into the object.

Popular explanation:

When an object of class B is used in class A, it is generally necessary to explicitly new an object of class B in the code of class A. This way is created by ourselves. The initiative to create a cooperative object is in our hands. We can create the object we need. The initiative and creation time of creating an object are controlled by ourselves. In this way, the coupling degree between objects will be high. Object a needs to use object B to accomplish one thing together. Object a needs to use B, Then a is dependent on B, that is, there is a coupling relationship between a and B, and they are closely coupled.

public class A
{
private B b = new B();// Active new is an object of B. Create it on your own initiative
public void Get()
{
B.Create();
}
}

After using dependency injection technology, a’s code only needs to define a private B object, and it doesn’t need to get the object by new directly. Instead, it uses the relevant container control program to get the B object out of the external new and inject it into the reference in class A. Now the creation of objects is controlled by a third party. Whatever objects you want, it will give you. The dependency will change, the original dependency will be gone, and the coupling between a and B will be reduced.

public class A
{
private B b;// The external new is injected into the reference
public void Get()
{
B.Create();
}
}

3.3 relationship

Inversion of control (IOC)It is a software design pattern that guides us to design better and more loosely coupled programs,

And concreteImplementation modeyesDependency injectionandDependency lookup

In this article, I mainly talk about the common onesDependency injectionThe way.

In actual development, you may also hear another term calledIOC containerThis is actually a dependency injectionframe

Used to map dependencies, manage object creation and lifetime( It will be explained in the following chapters.)

4、 Thinking

When it comes to dependency, what’s the difference between dependency injection and factory mode?

This is an example of comparison on the Internet

Factory design pattern Dependency injection
objects creating It is used to create objects. We have a separate factory class that contains the creation logic. It is responsible for creating and injecting objects.
The state of the object It is responsible for creating stateful objects. Responsible for creating stateless objects
Runtime / compile time Creating objects at compile time Configure objects at run time
Code change If business requirements change, the object creation logic may change. There is no need to change the code
mechanism Classes depend on factory methods, which in turn depend on concrete classes The parent and all dependent objects can be created in a single location

Well, that’s all for this article,In the following chapters, the commonly used IOC containers will be explainedHope to help you.

If there is something wrong or incomprehensible, I hope you can correct it, raise questions, discuss it together, keep learning and make progress together.