Wednesday, June 18, 2008

Container Dependency anti-pattern

It is good design to minimize the number of dependencies between components in a system. Dependency Injection is used to decouple components from each other. A component should not specify, or require, a particular implementation of a Data Service, Logging Service or any other cross-cutting concern.

One solution to this problem is to use a DI container, such as Unity. This localizes all the dependencies in one place, the shell, or configuration point for your application. The components and services can be assembled and provided to the components as needed.

However, sometimes this leads to an anti-pattern in that components are built with a dependency on the container. This counteracts much of the advantage of using the container. It makes it difficult to share your components with another team that might be using a different container. Do not create container dependencies in your components!

The picocontainer example shows how to solve the problem with constructor injection, but assumes that the dependent container (B) only needs one instance of the A object. The question arises how can B create A objects on the fly as needed?

The solution is to inject a Factory into B via the constructor.

using System;
using Microsoft.Practices.Unity;

namespace DI
{
public class A
{
}

public class B
{
Factory<A> Afactory;

public B(Factory<A> f) { Afactory = f; }

public void SomeMethod()
{
A a = Afactory.Create();
Console.WriteLine(a.ToString());
}
}

public class Factory<T> where T : new()
{
public T Create() { return new T(); }
}
class Program
{
static void Main(string[] args)
{
UnityContainer unity = new UnityContainer();
B b = unity.Resolve<B>();
b.SomeMethod();
}
}
}


If you want to have the factory use a specific container, that could be an acceptable dependency, since it would be a trivial change to write a new Factory class for a different container. I will post this later.

See: http://www.codeplex.com/unity/Thread/View.aspx?ThreadId=29697 for a discussion about this.

UPDATE: See http://initializecomponent.blogspot.com/2008/10/common-service-locator.html

No comments: