Dependency Injection & IOC

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 MS 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 pattern 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 does it worth to put 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. Lets 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.
Lets suppose I want to create another class named AMDProcessor. Now how you would 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 type 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 have 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 cumbersome and makes 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 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()
    {
        //resolve available IProcessor
    }
}
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 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 :

1. Create object of IProcessor (using actual implementation)
2. Create an object of Computer
3. Inject the object of IProcessor to Computer
4. 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);

        //Configure if any.
        return this.CurrentComputer;
    }
}

So you can think a container as a higher level of abstraction, wrapping around all the objects that needs dependency injection and resolves 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.
Shout it Submit this story to DotNetKicks Bookmark and Share
Read Disclaimer Notice

0 comments:

Post a Comment

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.

Author's new book

Abhishek authored one of the best selling book of .NET. It covers ASP.NET, WPF, Windows 8, Threading, Memory Management, Internals, Visual Studio, HTML5, JQuery and many more...
Grab it now !!!