Instead of dwelling on why the term "Inversion of control" was chosen, I will instead tell you what problem it tries to solve and how it solves it. You can come up with your own explanation of what the original intention of the term was. (Please share it with us in the comments below if you don't mind )
Aim of Dependency Inversion:
Referring to the image below. Suppose when the console application was initially built, there was no XML or Text File. But later on an XML datasource is added (and a corresponding GetMessageFromXML class). And a few more months down the line a text file datasource. Our aim is write the code in a way that, when a new datasource is added, only the calling datasource from Main method needs to be updated. The code in DisplayMessage Class shouldn't need to change to accommodate a new datasource.
Bad code without Dependency Inversion:
What I will try to show you here is how in a bad code, changing one part of the code requires changes in other parts of the code. And how after applying dependency inversion, I could make different parts of code, independent of each other.
using System;
using System;
Inversion of Control attempts to make the class DisplayMessage and the data fetching classes totally independent of each other.
Code with Dependency Inversion:
Now like you see above, injecting the dependencies through the constructor, I am decoupling DisplayMessage class from the class GetMessageFromDatabase or any other class that the DisplayMessage class might end up utilizing. I can add as many new classes as I want, to fetch data, without modifying the DisplayMessage class, as long as they inherit from the IGetData interface.
So what happened here is that, everything depends on the interface. Which is ideal.
Now you might argue that I actually ended up shifting the dependency to the Main method. You are absolutely correct. My attempt was to strip this example to bare essentials. So that the root concept is easier to grasp. If you are curious to know, the dependency can be removed from the Main method, in multiple ways. One way being, to move the dependency to another class by applying the factory pattern, whose responsibility would be to pass the right IGetData object, based on the source.
Aim of Dependency Inversion:
Referring to the image below. Suppose when the console application was initially built, there was no XML or Text File. But later on an XML datasource is added (and a corresponding GetMessageFromXML class). And a few more months down the line a text file datasource. Our aim is write the code in a way that, when a new datasource is added, only the calling datasource from Main method needs to be updated. The code in DisplayMessage Class shouldn't need to change to accommodate a new datasource.
Bad code without Dependency Inversion:
What I will try to show you here is how in a bad code, changing one part of the code requires changes in other parts of the code. And how after applying dependency inversion, I could make different parts of code, independent of each other.
using System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
ConsoleApplication1
{
class Program
{
static void Main(string[]
args)
{
DisplayMessage
dm = new DisplayMessage();
dm.ShowMessage();
}
}
public class DisplayMessage
{
GetMessageFromDatabase
Gmd;
public
DisplayMessage()
{
Gmd = new
GetMessageFromDatabase();
}
public void ShowMessage()
{
Console.WriteLine(Gmd.GetMessage());
Console.ReadLine();
}
}
public class GetMessageFromDatabase
{
public string GetMessage()
{
//Pretend
this comes from the database
return
"Hi from database";
}
}
}
Everything works fine. Suppose 3 months down the line based on the argument passed into the Main method, I have to pull data either from a database or an xml file. To make that happen I add a class GetMessageFromXML to my code. Also I make a slight modification to my Main method. The code now looks like below.
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
ConsoleApplication1
{
class Program
{
static void Main(string[]
args)
{
DisplayMessage
dm = new DisplayMessage(args[0].ToString());
dm.ShowMessage();
}
}
public class DisplayMessage
{
string
source;
public
DisplayMessage(string s)
{
source = s;
}
public void ShowMessage()
{
if
(source.ToUpper() == "DATABASE")
{
GetMessageFromDatabase
Gmd = new GetMessageFromDatabase();
Console.WriteLine(Gmd.GetMessage());
Console.ReadLine();
}
else
if (source.ToUpper() == "XML")
{
GetMessageFromXML
Gmx = new GetMessageFromXML();
Console.WriteLine(Gmx.GetMessage());
Console.ReadLine();
}
}
}
public class GetMessageFromDatabase
{
public string GetMessage()
{
//Pretend
this comes from the database
return
"Hi from database";
}
}
public class GetMessageFromXML
{
public string GetMessage()
{
//Pretend
this comes from an XML file
return
"Hi from XML";
}
}
}
Now another 2 months later suppose I also need to read data from a text file based on the input parameter to the main method. What would I do? I would add another class GetMessageFromTextFile . Also I would have to modify the class DisplayMessage.
Do you see how dependent the class DisplayMessage is on the implementation (that is depending on where the data is being fetched from) ?
Inversion of Control attempts to make the class DisplayMessage and the data fetching classes totally independent of each other.
There are three popular ways of inversion of control namely
- Constructor Injection
- Method Injection
- Property Injection
Code with Dependency Inversion:
In the code below, I have demonstrated inversion of control using constructor injection
using System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
ConsoleApplication1
{
public interface IGetData
{
string
GetMessage();
}
class Program
{
static void Main(string[]
args)
{
IGetData
IG;
string
source = args[0].ToString();
if
(source.ToUpper() == "DATABASE")
{
IG = new
GetMessageFromDatabase();
}
else
if (source.ToUpper() == "XML")
{
IG = new
GetMessageFromXML();
}
else
if (source.ToUpper() == "TEXT")
{
IG = new
GetMessageFromTextFile();
}
else
{
IG = new
GetMessageFromDatabase();//default set to database
}
DisplayMessage
dm = new DisplayMessage(IG);
dm.ShowMessage();
}
}
public class DisplayMessage
{
IGetData
IGLocal;
public
DisplayMessage(IGetData IG)
{
IGLocal = IG;
}
public void ShowMessage()
{
Console.WriteLine(IGLocal.GetMessage());
}
}
public class GetMessageFromDatabase
: IGetData
{
public string GetMessage()
{
//Pretend
this comes from the database
return
"Hi from database";
}
}
public class GetMessageFromXML
: IGetData
{
public string GetMessage()
{
//Pretend
this comes from an XML file
return
"Hi from XML";
}
}
public class GetMessageFromTextFile
: IGetData
{
public string GetMessage()
{
//Pretend
this comes from an Text file
return
"Hi from Text file";
}
}
}
Now like you see above, injecting the dependencies through the constructor, I am decoupling DisplayMessage class from the class GetMessageFromDatabase or any other class that the DisplayMessage class might end up utilizing. I can add as many new classes as I want, to fetch data, without modifying the DisplayMessage class, as long as they inherit from the IGetData interface.
So what happened here is that, everything depends on the interface. Which is ideal.
Now you might argue that I actually ended up shifting the dependency to the Main method. You are absolutely correct. My attempt was to strip this example to bare essentials. So that the root concept is easier to grasp. If you are curious to know, the dependency can be removed from the Main method, in multiple ways. One way being, to move the dependency to another class by applying the factory pattern, whose responsibility would be to pass the right IGetData object, based on the source.


Excellent!!
ReplyDeleteSimple.Straight forward explanation of IoC concept with example!!! well done.
ReplyDeleteThank you Esho :)
ReplyDelete