Before even we get in to abbreviation of IOC and DIP, let's first
understand the problem. Consider the below example we have a customer
class which contains an address class object. The biggest issue with the
code is tight coupling between classes. In other words the customer
class depends on the address object. So for any reason address class
changes it will lead to change and compiling of '
ClsCustomer
' class also. So let's put down problems with this approach:
- The biggest problem is that customer class controls the creation of address object.
- Address class is directly referenced in the customer class which leads to tight coupling between address and customer objects.
- Customer class is aware of the address class type.
So if we add new address types like home address, office address it
will lead to changes in the customer class also as customer class is
exposed to the actual address implementation.
Figure: - Problems of IOC
So if for any reason the address object is not able to create the
whole customer class will fail in the constructor initialization itself.
Solution
DI is also known as
Inversion of Control (IOC)
Dependency injection is a design pattern basically allows us to create loosely coupled,
reusable, and testable objects in your software designs by removing
dependencies.
Instead of compile time dependencies it offers runtime loading and
initialization of components, which makes solutions load on demand.
Principles of IOC
The basic principle of IOC stands on the base of Hollywood principle (response given to amateurs auditioning in Hollywood):
In other words it like address class saying to the customer class, do not create me I will create myself using some one else.
We will take a look into the Object dependencies before digging in more.
Consider a scenario of fetching an employee details and show display in
UI. Let us say create a Business logic layer class named EmployeeBAL and
a data access layer class named EmployeeDAO
public class EmployeeDao
{
//Some code
}
public class EmployeeBAL
{
var employeeDAO = new EmployeeDao();
//Some code
}
From the above code you will notice one thing that we are creating
EmployeeDAO instance inside the Business logic layer class. So here
comes the dependency
What is wrong if we have a dependency?
Think about whether your code is unit testable. We cannot fully unit
test the EmployeeBAL as it has a dependency on Employee DAO. So we can
say as long as the composition of the DAO exists within the BAL we
cannot unit test the EmployeeBAL.
You will also notice one more thing here; with this type of implementation you will see a high coupling of BAL and DAL.
How to make it loose coupling?
The basic idea behind Dependency Injection is that you should isolate
the implementation of an object from the construction of objects on
which it depends.
Coming to the example, we should be isolating the implementation of
EmployeeBAL object and the construction of the dependent EmployeeDAO
object.
We will see how we can make loosely coupled objects in detail
Different types of Dependency Injection
- Constructor Injection
- Setter Injection
- Interface-based injection
Constructor based dependency injection
We will have to modify the EmployeeBAL to accept an EmployeeDAO instance within its constructor.
public class EmployeeDao
{
//Some code
}
public class EmployeeBAL
{
EmployeeDao employeeDAO;
public EmployeeBAL(EmployeeDAO employeeDao){
this.employeeDAO = employeeDao;
}
//Some code
}
Property based dependency injection
With property based injection we will have a public getter and setter
Property of type EmployeeDao so that the dependency can be externally
set.
public class EmployeeBAL
{
Public EmployeeDao EmployeeDataAccess{ get; set; }
}
var employeeBAL = new EmployeeBAL();
EmployeeBAL.EmployeeDataAccess = new EmployeeDao();
Wait!!!
The above ones are just some techniques of injecting the dependency.
We are still yet to discuss one more interesting thing Unit Testing.
Are you agreeing that we have removed the DAO creation from the
Business Logic EmployeeBAL? Yes it is good but it still depends on the
actual instance of EmployeeDao.
Consider the below mentioned implementation of the same sample senarios
interface IDataAccess
{
//Some code
}
class EmployeeDao : IDataAccess
{
//Some code
}
public class EmployeeBAL
{
private IDataAccess dataAccess;
public BusinessFacade(IDataAccess dao)
{
dataAccess = dao;
}
}
You can notice we are doing a constructor dependency injection but most important thing here
is we are using Interface type than creating a strongly typed object.
The advantage that we are getting here is we can have an in memory data access object of
IDataAccess interface type and we can easily inject the dependency to the EmployeeBAL.
By this way we no need to have the actual database dependency.
Are you happy that we can unit test the BAL without the data access dependency?
Interface Injection
Interface injection, by using a common interface that other classes need to implement to inject dependencies.
The following code shows an example in which the classes use the
ICreditCard interface as a base contract to inject an instance of any of
the credit card classes (VISA or MasterCard) into the
CreditCardValidator class. Both the credit card classes VISA and
MasterCard implement the ICreditCard interface:
01.
public
interface
ICreditCard
02.
{
03.
string
CardNo {
set
; }
04.
bool
Validate();
05.
}
06.
07.
public
class
MasterCard : ICreditCard
08.
{
09.
private
string
_cardno;
10.
public
bool
Validate()
11.
{
12.
return
true
;
13.
}
14.
15.
public
string
CardNo
16.
{
17.
set
{ _cardno = value; }
18.
}
19.
20.
}
21.
public
class
VISA : ICreditCard
22.
{
23.
private
string
_cardno;
24.
25.
public
bool
Validate()
26.
{
27.
return
true
;
28.
}
29.
30.
public
string
CardNo
31.
{
32.
set
{ _cardno = value; }
33.
}
34.
}
Dependency Injection (or DI) allows us to provide implementations and
services to other classes for consumption in a very loosely-coupled
way. The key tenet is that such implementations can be swapped out for
other implementations by changing a minimal amount of code, as the
implementation and the consumer are linked by
contract only.
In C#, this means that your service implementations should adhere to
an interface, and when creating consumers for your services you should
program against the
interface and not the
implementation, and require that the implementation is provided for you, or
injected
rather than having to create instances yourself. Doing this allows your
classes to not worry about how dependencies are create nor where they
come from; all that matters is the contract.
Dependency Injection by Example
Let’s go through a simple example where DI could be useful. First, let’s create an interface (the
contract) which will allow us to perform some task, say logging a message:
void LogMessage( string message); |
Notice that nothing about this interface describes how the message is
logged and where it is logged to; it simply has the intention of
recording a string to some repository. Next, lets create something which
uses this interface. Say we create a class which watches a particular
directory on disk, and logs a message whenever the directory is changed:
public class DirectoryWatcher |
private FileSystemWatcher _watcher; |
public DirectoryWatcher(ILogger logger) |
_watcher = new FileSystemWatcher( @"C:\Temp" ); |
_watcher.Changed += new FileSystemEventHandler(Directory_Changed); |
void Directory_Changed( object sender, FileSystemEventArgs e) |
_logger.LogMessage(e.FullPath + " was changed" ); |
The key thing to notice is that the constructor, we require that something which implements
ILogger
is given to us, but again notice that we don’t care about where the log
goes or how it is created. We can just program against the interface
and not worry about it.
This means that in order to create an instance of our
DirectoryWatcher we must also have an implementation of
ILogger ready. Let’s go ahead and create one which logs messages to a text file:
public class TextFileLogger : ILogger |
public void LogMessage( string message) |
using (FileStream stream = new FileStream( "log.txt" , FileMode.Append)) |
StreamWriter writer = new StreamWriter(stream); |
writer.WriteLine(message); |
Let’s create another which logs messages to the Windows Event Log:
public class EventFileLogger : ILogger |
private string _sourceName; |
public EventFileLogger( string sourceName) |
_sourceName = sourceName; |
public void LogMessage( string message) |
if (!EventLog.SourceExists(_sourceName)) |
EventLog.CreateEventSource(_sourceName, "Application" ); |
EventLog.WriteEntry(_sourceName, message); |
Now we have two separate implementations which log messages in very different ways, but both implement
ILogger, which means that either one can be used where an instance of
ILogger is required. Now we can create an instance of
DirectoryWatcher and have it use one of our loggers:
ILogger logger = new TextFileLogger(); |
DirectoryWatcher watcher = new DirectoryWatcher(logger); |
Or, by just changing the right-hand side of the first line we can use our other implementation:
ILogger logger = new EventFileLogger(); |
DirectoryWatcher watcher = new DirectoryWatcher(logger); |
This happens without any changes to the implementation of
DirectoryWatcher, and this is the key concept. We are
injecting
our logger implementation into the consumer, so that the consumer
doesn’t have to create this instance on its own. The example shown is
trivial, but imagine using this in a large-scale project where you have
several dependencies which need to be used by many times more consumers,
and then suddenly a requirement comes along which means that the method
of logging a message must change (say the messages are required to be
logged into Sql Server for auditing purposes). Without some form of
dependency injection, you will have to carefully examine the code and
change anything which actually creates an instance of a logger and then
uses it. In a large project this can be painful and error prone. With
DI, you would just have to change the dependency in one place, and the
rest of your application will effectively absorb the change and
immediately start using the new logging method.
Essentially, it solves the classic software problem of
high-dependency and allows you to create a loosely-couple system which
is extremely agile and easy to change.
Advantages of Dependency Injection
The primary advantages of dependency injection are:
Loose coupling
Adds potential flexibility to a codebase for future changes
Centralized configuration
Easily testable
Dependency Injection Containers
Many DI frameworks which you can download and use go a step further
and employ the use of a Dependency Injection Container. This is
essentially a class which stores a mapping of types and returns the
registered implementation for that type. In our simple example we would
be able to query the container for an instance of
ILogger and it would return an instance of TextFileLogger, or whichever instance we had initialised the container with.
This has the advantage that we can register all of our type mappings
in one place, usually in an “Application Start” event, and that gives us
quick and clear visibility as to what dependencies we have in the
system. Also, many professional frameworks allow us to configure the
lifetime of such objects, either creating fresh instances every time we
ask for one, or re-using instances across calls.
The container is usually created in such a way that we can get access
to the ‘Resolver’ (the thing which allows us to query for instances)
from anywhere in the project.
Finally, professional frameworks usually support the concept of
“sub-dependencies”, where a dependency has itself one or more
dependencies to other types also known to the container. In this case,
the resolver can fulfil these dependencies too, giving you back a full
chain of correctly created dependencies according to your type mappings.
IOC Frameworks
1. Unity Application Block (Unity)
2. NInject
3. StructureMap
4. Castle Windsor
5. Spring.NET
6. AutoFaq
7. Picocontainer.NET
8. LinFu
9. Puzzle.NFactory