When we talk about software programs they have LOGIC’s and LOGIC’s have FLOW. Take the below simple code. We have a “Customer” class which is instantiated from a console application.
If you watch the program flow of the customer object it is as follows:-
The biggest benefit achieved by the above approach is “Decoupling”. You can now invoke the customer object and pass any kind of “Logger” object as shown in the below code.
class Customer
{
private SQLServer Dal =
new SQLServer();
public bool validate()
{
return true;
}
public void Add()
{
if (validate())
{
Dal.Add();
}
}
}
class SQLServer
{
public void Add()
{
}
}
Customer obj = new Customer();
obj.Add();
If you watch the program flow of the customer object it is as follows:-
- Client creates object of the customer class.
- Customer class creates the object of SQL Server data access layer.
- Customer object then invokes “Validate” method.
- Once the “Validate” method is successful then it invokes the “Add” method.
In other words the “Customer” class is in control of his FLOW. Now let us complicate the situation. Let’s say we have different flavors of data access layer. So let’s say we have “SqlServer” and “Oracle” as shown below.
So the best way to implement the same is my creating a generic interface “Dal” and pointing that generic interface to “SQLServer” or “Oracle” object depending on situations. So if you see the flow now:-
- Client creates object of the customer class.
- Customer class depending on configuration decided to create SQL Server object or Oracle object.
- Customer object then invokes “Validate” method.
- Once the “Validate” method is successful then it invokes the “Add” method.
A good code is that code which takes care of his own logic rather than overloading with logic which he is not concerned with. For instance in this case the customer class is now taking care of logics like deciding which data access layer objects to be created.
class Customer
{
private Dal dal;
public Customer(int
DalType)
{
if (DalType == 1)
{
dal = new
SQLServer();
}
else
{
dal = new
Oracle();
}
}
public bool validate()
{
return true;
}
public void Add()
{
if (validate())
{
dal.Add();
}
}
}
interface Dal
{
void Add();
}
class SQLServer : Dal
{
public void Add()
{
}
}
class Oracle : Dal
{
public void Add()
{
throw new
NotImplementedException();
}
}
This makes the customer class tightly coupled to the data access layer changes and second in long run this code becomes difficult to maintain. So the solution here is customer class should perform his own concerned logic like validating customer class , calling add method of data access layer. But deciding which data access layer to create is not his part.
So if this logic or this control is given to some other entity or can be INVERTED to some one else then the code would look much better.
So if we change the code to something as shown below. Where the customer class say’s hey look I am not concerned its Oracle or SQL Server. Pass me the generic interface object from the constructor and relieve me from the decision making logic of Data access layer.
class Customer
{
private Dal dal;
public Customer(Dal obj)
{
dal = obj;
}
…
…
}
So now it’s the work of client or the invoker to decide which data access layer object has to be passed.
If you visualize this scenario the flow of “Customer” class now takes care of his logic and the unconcerned logic is now passed to an external entity. This is termed as “Inversion of control”. The control or logic which is not part of that entity is taken care by someone else.
Also if you put in other terms the flow of “Customer” class is now affected by external entities depending on whether they inject “SqlServer” object or “Oracle” object.
A word of caution here, do not conclude that IOC can only be implemented by exposing abstraction via constructor. You can delegate the control flow by callback delegates, observer pattern, events, and lot of other ways.
The above way of sending abstraction from constructor is termed as “dependency” injection.IOC (Inversion of control) is a general parent term while DI (Dependency injection) is a subset of IOC.
DI provides objects that an object needs. So rather than the dependencies construct themselves they are injected by some external means. For instance let’s say we have the following below class “Customer” who uses a “Logger” class to log errors. So rather than creating the “Logger” from within the class, you can inject the same via a constructor as shown in the below code snippet.
The biggest benefit achieved by the above approach is “Decoupling”. You can now invoke the customer object and pass any kind of “Logger” object as shown in the below code.
Customer obj = new Customer(new EmailLogger());
Customer obj1 = new Customer(new EventViewerLogger());
Inversion of
control
|
Dependency
injection
|
It’s a generic term and implemented in several ways
(events, delegates etc).
|
DI is a subtype of IOC and is implemented by constructor
injection, setter injection or method injection.
|