Well, after I introduced Inversion of Control with few simple examples in my previous post, I thought it would be nice to take this discussion further with more implementation of Dependency Injection Principles. Later, I would also take a look at some of the existing frameworks available with Microsoft Patterns and Practices which follow these well known principles.
If you remember my previous post on Inversion of control with interfaces, I have clearly stated that we could invert the control to some other module to make strong decoupling on one class with the other. In this post, I will first cover the basics of the design principle on Dependency Injection and later talk about DI containers.
Dependency Injection
If you know about traditional Design Patterns, you should already know few of the most used patterns called Factory. Factory pattern allows you to put your object creation into a special module called Factory. Thus your objects will be created just inside your factory classes. But is it worth putting all the object creation inside your factory classes? That means each of those classes will hold reference of all the objects that needed to be created. Let's see the code to show up what I am talking about:
public enum ProcessorType
{
x86,
x64
}
public class Computer
{
public IntelProcessor GetProcessor(double speed, ProcessorType type, string version)
{
return new IntelProcessor { Speed = speed, Type = type, Version = version };
}
}
public class IntelProcessor
{
public string Version { get; set; }
public ProcessorType Type { get; set; }
public double Speed { get; set; }
public string GetProcessorInfo()
{
return string.Format("{0} Ghz Processor for {1}, v{2}",
this.Speed, this.Type, this.Version);
}
public override string ToString()
{
return this.GetProcessorInfo();
}
}
Here I have a class called Computer
which creates an object of a Processor
. Every processor
has Version
, Type
and Speed
properties in it and hence the object is constructed with these values. Therefore, following the creational principle, the method follows a Factory
. Is it? No. There is a problem in the code.
Let's suppose I want to create another class named AMDProcessor
. Now how would you write the code:
public class Computer
{
public IProcessor GetProcessor(double speed, ProcessorType type,
string version, string brand)
{
IProcessor processor = null;
switch(brand)
{
case "INTEL":
processor = new IntelProcessor
{ Speed = speed, Type = type, Version = version };
break;
case "AMD":
processor = new AMDProcessor
{ Speed = speed, Type = type, Version = version };
break
}
return processor;
}
}
Thus I have created one Interface named IProcessor
and made each of the Processor
types to implement the IProcessor
interface. The brand string
parameter coming from to the method GetProcessor
identifies which type of processor class to construct. The IProcessor
interface will look like:
interface IProcessor
{
string Version { get; set; }
double Speed { get; set; }
ProcessorType Type { get; set; }
string GetProcessorInfo();
}
But there still lies a problem. When we talk about the Computer
class, it has strong coupling with the actual types namely AMDProcessor
or IntelProcessor
Types. Hence if you want to include any other processor, you need to change the code for Computer
class and include its type and recompile the project. Hence, it makes it cumbersome and makes it almost impossible to maintain such classes and also those classes are not flexible enough to handle Runtime
Types.
Dependency Injection solves the issue by putting the creation of object outside the scope of the container. So if I implement the same class using Dependency Injection, it would look like:
public enum ProcessorType
{
x86,
x64
}
public class Computer
{
public IProcessor CurrentProcessor { get; set; }
public Computer(IProcessor processor)
{
this.CurrentProcessor = processor;
}
public IProcessor GetProcessor()
{
return this.CurrentProcessor;
}
}
public class IntelProcessor : IProcessor
{
public string Version { get; set; }
public ProcessorType Type { get; set; }
public double Speed { get; set; }
public string GetProcessorInfo()
{
return string.Format("{0} Ghz Processor for {1}, v{2}",
this.Speed, this.Type, this.Version);
}
public override string ToString()
{
return this.GetProcessorInfo();
}
}
Here, we have modified the object creation of dependent object IProcessor
outside the scope of Computer
which is more generalized. Now, if we expose the interface IProcessor
, it is free to take any form way later than when the Computer
class is compiled.
So the object will be created outside the scope of the computer but will inject the external object into the computer.
IntelProcessor processor = new IntelProcessor
{ Speed = 1.2, Type = ProcessorType.x64, Version = "3.5.6" };
Computer computer = new Computer(processor);
Clearly in the above implementation, I have used Constructor
based DI implementation, but you can easily modify it to use Getter
/Setter
method using the Default constructor followed by the assignment of CurrentProcessor
property.
IntelProcessor processor = new IntelProcessor
{ Speed = 1.2, Type = ProcessorType.x64, Version = "3.5.6" };
Computer computer = new Computer();
computer.CurrentProcessor = processor;
Or even we use separate ServiceLocator
class to get the object of IProcessor
from an abstract
implementation of a Locator
method.
public class ServiceLocator
{
public static IProcessor GetProcessor()
{
}
}
Computer computer = new Computer();
computer.CurrentProcessor = ServiceLocator.GetProcessor();
The ServiceLocator
class takes the responsibility of resolving the available runtime IProcessor
object available.
Note: You should not confuse ServiceLocator with Factory implementation. ServiceLocator is used to Locate and for implementation rather than creating a fixed set of types like Factory.
DI Containers
While talking about DI, I have told you that we need to inject one object within another. The DI container acts as a level of abstraction of the object creation. The Container actually handles object creation, association and configuration. Hence, if I have to write a container, I would:
- Create object of
IProcessor
(using actual implementation) - Create an object of
Computer
- Inject the object of
IProcessor
to Computer
- Configure
Computer
if any
public class ComputerContainer
{
public Computer CurrentComputer { get; set; }
public IProcessor CurrentProcessor { get; set; }
public Computer GetComputer(IProcessor processor)
{
this.CurrentProcessor = processor;
this.CurrentComputer = new Computer(processor);
return this.CurrentComputer;
}
}
So you can think of a container as a higher level of abstraction, wrapping around all the objects that needs dependency injection and resolve each of them internally.
Conclusion
I am thinking of a separate post for implementation of Containers using Unity Framework. So, this is all about DI, I hope the post makes sense. Feel free to write your feedback, any addition to thoughts are welcome.
Thanks for reading the post.