Background
In recent days the concept of dependency injection or inversion of control is much talked about in object oriented world of programming. I decided to give an introduction of this concept to people who want to understand this concept with little effort and use it effectively.
Introduction
So what is dependency injection anyway? Simply put, it is the capability of a consumer to consume a component without knowing in advance the specifics/origination of the consumed component.
Now you would ask, what’s new about this? Isn’t this why we have interfaces? I will explain this more clearly by taking an example of a design pattern that is much closer to dependency injection design pattern; that is object creation or factory framework. Consider a typical factory pattern as shown in the figure below.
However, there is a subtle difference between the factory pattern shown above and truly desired object oriented abstraction. The fact that the class factory actually knows about the consumed class (which implements the desired interface that was originally requested by the consumer) breaks this abstraction and plug and play feature. This is exactly where dependency injection framework comes in.
Motivation
The motivation for dependency injection framework comes from the concepts of "Inversion of Control" and extensibility. Applications now a days are providing more and more extension points to enrich functionality of solutions. Extended plug-ins in such cases really take on the control from the container that hosts them in the first place (hence the term "Inversion of Control"). Hence dependency injection enables abstraction of dependencies at design time from dynamic binding at runtime. This way extension points can be instantiated, injected as properties or in constructor of the dependent object with dynamic configuration. A great example of such an application is Eclipse (www.eclipse.org).
Dependency Injection Frameworks
In the dependency injection framework, the consumer specifies a set of dependencies that it requires to be fulfilled in order to perform an operation. During runtime a configuration / declarative effort is required to specify consumable objects that can fulfill a set of dependencies. An assembler / runtime component can then perform the necessary binding prior to code execution or notify failure via exceptions. Following figure depicts this behavior.
Dependency injection frameworks really was made possible by new generation object oriented languages that supported metadata access capabilities such as reflection and dynamic code generation / loading. The idea however is much older and can be seen in dynamic linked libraries and even COM.
There are many frameworks that aid in dependency injection. I will discuss one of the major framework known as Spring.NET (http://www.springframework.net/). Spring.NET implements many proven design patterns that makes building enterprise applications easier. But I will just focus on dependency injection part of Spring.NET.
A Simple Example of Dependency Injection Using Spring.NET
Trust me even sample examples that ship with Spring.NET aren’t simple. So I decided to make a small startup example to demonstrate how we can use Spring.NET to solve factory creation scenario discussed above.
For building this code I used Visual Studio.NET 2008 along with Spring.NET binaries that I obtained from http://www.springframework.net.
I then created a sample console project called SpringDotNETExample and added reference to Spring.Core assembly to the project (this assembly can be found in the bin folder of your Spring.NET installation directory; for me, it was c:\Program Files\Spring.NET 1.1.2\bin\net\2.0\debug\Spring.Core.dll).
The scenario is very simple. We have a generic interface ISayHello
that has a string property SayHello
as follows:
namespace SpringDotNETExample
{
public interface ISayHello
{
String SayHello { get; }
}
}
I then created a class WorldSayHello
that implements ISayHello
as following:
namespace SpringDotNETExample
{
public class WorldSayHello : ISayHello
{
public string SayHello
{
get { return "Hello Friend!"; }
}
}
}
At runtime I want to bind my configuration to create WorldSayHello
as concrete implementation for ISayHello
. But this mapping will be transparent to my main program which will use Spring.NET assembler to request instance of mapped object. For this example I would assume that main program requests this object via key “ISayHelloInterface
” that must correspond to the mapping.
To create the mapping I created a mapping in my application configuration file app.config. Following are the contents of this file. I have bolded the interesting parts of this file, rest are just Spring.NET essentials that are less interesting.
="1.0" ="utf-8"
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context"
type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects"
type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<description>An example that demonstrates simple IoC features.
</description>
<object name="ISayHelloInterface"
type="SpringDotNETExample.WorldSayHello, SpringDotNETExample"/>
</objects>
</spring>
</configuration>
Now my program simply requests the object mapped to key ISayHelloInterface
to obtain an implementation of ISayHello
interface. Notice that the mapping can be changed without compiling the code. This can provide immense versioning capabilities. Following is my main program:
using System;
using Spring.Context;
using Spring.Context.Support;
namespace SpringDotNETExample
{
class Program
{
static void Main(string[] args)
{
IApplicationContext ctx = ContextRegistry.GetContext();
ISayHello hello = (ISayHello)ctx.GetObject("ISayHelloInterface");
Console.WriteLine(hello.SayHello);
}
}
}
Running the program yields the following output. :)
Hello Friend!
Press any key to continue . . .
In my next articles, I will discuss some more advanced concepts in dependency injection such as constructor injection. So stay tuned!