Introduction

Definition: “Inversion of Control is an abstract principal describing an aspect of some software architecture design in which the flow of control
of a system is inverted in comparison to procedural programming.”

What happens in procedural programming is a chunk of code that uses or consumes another chunk of code is in control of
the process.
It knows what piece of code, which method, in which class, it uses and this way
it knows about the implementation details in the code it uses.

Example

Here is an example of such a scenario when a class X uses another class Y. The consumer class X needs to consume the class Y to accomplish
something. This is a very obvious example, but wait and think for a minute, does X really need to know that it uses Y? Isn’t it enough that X knows that
it uses something that has the behavior, the methods, the properties, etc., of Y without knowing who actually implements the behavior?
Extract the abstract
definition of the behavior used by X in Y and let the consumer X to use an instance of that behavior instead of Y. This way X does
the same thing without knowing the specifics about Y.

As illustrated above, class Y implements interface I and class X uses I. An interesting
thing is though class X still uses class Y through interface I,
X doesn’t know that it uses Y, it just knows that it uses something that implements I.
Right now, it’s Y, but it could be A, B, or C that implements interface I.
This way we are reducing the dependency of X over Y. Now you might have a question in your mind, what is dependency? And how is
it dangerous for software?

What is Dependency in terms of classes?

The example mentioned in the start of this article is an example of dependent classes. When class X uses a method of class Y or class Y as
a whole, we can say that
class X has some level of dependency over class Y. Dependency may extend to several levels. X uses Y, Y uses A and B, A uses C, and more. This way this chain
goes on and on. The problem with this is if we have any change in these classes, it may spawn to multiple classes.
The solution to this dependency problem is define a loose relation
between classes. One way to achieve this is the Inversion of Control pattern. This pattern uses Dependency Injection to eliminate tight coupling between objects.
Let’s see dependency with some practical example.

A sample scenario

We have a class called LoggerEngine which is used for logging messages. It has a method called
Log which receives a string object as an argument and logs it into
a file
using another class named FileLogger.

The code for LoggerEngine looks like this:

Notice the above code in which the Log method creates an instance of
the FileLogger class and then logs the message using its own Log method. Here you may define all three methods
to be static or there might be a singleton instance ofthe FileLogger class. What matters
is, the dependency of the LoggerEngine class over the FileLogger class.
We can solve this by creating an abstract definition of the behavior in FileLogger that
LoggerEngine needs in the form of an interface ILogger and making
the Log method
use an instance of ILogger instead of an instance of FileLogger.

Now LoggerEngine no longer knows about the FileLogger class. It just uses
an instance of the ILogger interface of which FileLogger is one of many possibly implementations.
Now in fact we could easily change it so that LoggerEngine logs messages on
the console instead of a file without changing a single line of code in LoggerEngine.
This all sounds good, but the main question is how does LoggerEngine get
an instance of ILogger? Here we need a way for LoggerEngine to get an instance of
ILogger
at runtime without knowing about the concrete implementation. There are a couple of ways to do that, one of which is through Dependency Injection.

Dependency Injection

This is the basic concept for implementing the Inversion of Control (IoC) pattern.

It eliminates tight coupling between objects.

Makes objects and application more flexible.

It facilitates creating more loosely coupled objects and their dependencies.

Constructor Injection

Here the object reference would get passed to the constructor of the business class
LoggerEngine. In this case since the LoggerEngine class depends on
the FileLogger class,
a reference of the FileLogger class will pass steps to implement Constructor Injection.

Shortcoming of Constructor Injection

In constructor injection, the business logic class doesn’t have a default constructor.

Once the class is instantiated, object dependency cannot be changed.

Setter Injection

This uses properties to inject a dependency. Here rather than creating a reference and assigning
it in the constructor, it is done in properties.
This way the LoggingEngine class could have a default constructor.

Here we have a static write-only property Logger which is used for setting
the value of reference of the ILogger instance and a static method
which is used
for getting the value of reference of the ILogger instance.

Generic-Type Injection

In this method, either we can define a Generic LoggerEngine class or
a Generic method in the LoggerEngine class. A Generic method would be more appropriate because
if we use a Generic class then it will cause the same problems which are identified in Constructor Injection.

Comments and Discussions

A whole lot of talk but really very poor explanations (assuming you are targeting someone who doesn't how much about IOC already - otherwise what's the point?) and most importantly a complete lack of why the various approaches exist or where to use them.