Sunday, November 12, 2006

Way of implementing Dependency Injection

I am working on spring .NET for last of couple of months so thought to put my understanding of idea behind Sring.NET framework in following words.

Any cohesive application made of lots of reusable components. A good designer always dream of reducing the dependency among components in application. Dependency Injection allows coder to get rid of such menial tasks of creating of instances and setting the dependency of objects in code.

Dependency Injection using Factories:

Factories provide centralized approach to handle to instance creation and resolve the dependency among components and hence provide easier way of code maintenance which could have been big mess if such things solved in decentralized way. Using encapsulation Factories shields the information about object creation and it also hides the information about how factored components fit with each other in architecture of the application. All the client code has to do is to call create instance of factory method.

Following is sample code to achieve dependency injection using Factories:

Factory Code:

using System;

namespace DependencyInjectionFactory

{

public interface IFactoredProduct{}

public class ConcreteFactoredProduct:IFactoredProduct{}

public abstract class AbstractProduct

{

protected IFactoredProduct _factoredProduct;

protected AbstractProduct(IFactoredProduct factoredProduct){}

}

public class ConcreteProduct:AbstractProduct

{

public ConcreteProduct(IFactoredProduct factoredProduct)

:base(factoredProduct){}

}

public class ConcreteProductTwo:AbstractProduct

{

public ConcreteProductTwo(IFactoredProduct factoredProduct)

:base(factoredProduct){}

}

public enum AbstractFactoryEnum

{

ConcreteFactory1,

ConcreteFactory2

}

public abstract class AbstractFactory

{

public abstract AbstractProduct Create();

public static AbstractFactory GetFactory(AbstractFactoryEnum enumValue)

{

if(enumValue == AbstractFactoryEnum.ConcreteFactory1)

{

return new ConcreteFactory1();

}

else if(enumValue == AbstractFactoryEnum.ConcreteFactory2)

{

return new ConcreteFactory2();

}

return null;

}

}

public class ConcreteFactory1:AbstractFactory

{

public override AbstractProduct Create()

{

//Object creation and dependecy issue is solved here

return new ConcreteProduct(new ConcreteFactoredProduct());

}

}

public class ConcreteFactory2:AbstractFactory

{

public override AbstractProduct Create()

{

return new ConcreteProductTwo(new ConcreteFactoredProduct());

}

}

}

Client Code:

AbstractFactory factory =

AbstractFactory.GetFactory(AbstractFactoryEnum.ConcreteFactory1);

AbstractProduct product = factory.Create();

//Client code calls Create method of Factory

Console.WriteLine(product.GetType().ToString());

From boilerplate wiring point of view factories has following problems:

1) Instance creation code is hard coded in implementation of factory and thus makes it non extensible. Client code as seen in above code has to know what kind of factory has to be created.

2) All dependencies of objects are well known at compile time.

Dependency Injection using Containers:

Above problems can be solved by containers. Containers provides one layer of abstraction through allow client code to get rid of life cycle management and dependency issues. Containers are not new concept and have been in MS platform for long time now. MTS, CLR are some of the examples of heavy weight containers. In .NET world Spring.NET (http://www.springframework.net/) allows you to create custom light weight containers.

Following is the code which solves same problem described above (DI using factories) in bit different way.


Source Code:

using System;

namespace DependencyInjectionContainer

{

public interface IFactoredProduct{}

public class ConcreteFactoredProduct:IFactoredProduct{}

public abstract class AbstractProduct

{

protected IFactoredProduct _factoredProduct;

protected AbstractProduct(IFactoredProduct factoredProduct){}

}

public class ConcreteProduct:AbstractProduct

{

public ConcreteProduct(IFactoredProduct factoredProduct)

:base(factoredProduct){}

}

}

In Container approach instance creation and object plumbing is done thorough configuration file and thus avoid any hard coding like Factories approach.

xml version="1.0" encoding="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>

<object id="concreteProduct" type="DependencyInjectionContainer.ConcreteProduct,DependencyInjection">

<constructor-arg name="factoredProduct" ref="factoredInstance" />

object>

<object id="factoredInstance" type="DependencyInjectionContainer.ConcreteFactoredProduct,DependencyInjection">object>

objects>

spring>

configuration>

Client Code: The way Client code consumes created object is:

IApplicationContext appContext = ContextRegistry.GetContext();

Console.WriteLine(appContext.GetObject("concreteProduct").GetType().ToString());

More on “Dependency Injection” can be found in interesting article from Martin Fowler @ http://www.martinfowler.com/articles/injection.html#FormsOfDependencyInjection

No comments: