Why do I need an IoC container as opposed to straightforward DI code?

Viewed 274688

I've been using Dependency Injection (DI) for a while, injecting either in a constructor, property, or method. I've never felt a need to use an Inversion of Control (IoC) container. However, the more I read, the more pressure I feel from the community to use an IoC container.

I played with .NET containers like StructureMap, NInject, Unity, and Funq. I still fail to see how an IoC container is going to benefit / improve my code.

I'm also afraid to start using a container at work because many of my co-workers will see code which they don't understand. Many of them may be reluctant to learn new technology.

Please, convince me that I need to use an IoC container. I'm going to use these arguments when I talk to my fellow developers at work.

30 Answers

I'm with you, Vadim. IoC containers take a simple, elegant, and useful concept, and make it something you have to study for two days with a 200-page manual.

I personally am perplexed at how the IoC community took a beautiful, elegant article by Martin Fowler and turned it into a bunch of complex frameworks typically with 200-300 page manuals.

I try not to be judgemental (HAHA!), but I think that people who use IoC containers are (A) very smart and (B) lacking in empathy for people who aren't as smart as they are. Everything makes perfect sense to them, so they have trouble understanding that many ordinary programmers will find the concepts confusing. It's the curse of knowledge. The people who understand IoC containers have trouble believing that there are people who don't understand it.

The most valuable benefit of using an IoC container is that you can have a configuration switch in one place which lets you change between, say, test mode and production mode. For example, suppose you have two versions of your database access classes... one version which logged aggressively and did a lot of validation, which you used during development, and another version without logging or validation that was screamingly fast for production. It is nice to be able to switch between them in one place. On the other hand, this is a fairly trivial problem easily handled in a simpler way without the complexity of IoC containers.

I believe that if you use IoC containers, your code becomes, frankly, a lot harder to read. The number of places you have to look at to figure out what the code is trying to do goes up by at least one. And somewhere in heaven an angel cries out.

In my opinion the number one benefit of an IoC is the ability to centralize the configuration of your dependencies.

If you're currently using Dependency injection your code might look like this

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

If you used a static IoC class, as opposed to the, IMHO the more confusing, configuration files, you could have something like this:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Then, your Static IoC class would look like this, I'm using Unity here.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}

IoC Containers are also good for loading deeply nested class dependencies. For example if you had the following code using Depedency Injection.

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

If you had all of these dependencies loaded into and IoC container you could Resolve the CustomerService and the all the child dependencies will automatically get resolved.

For example:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}

I'm a fan of declarative programming (look at how many SQL questions I answer), but the IoC containers I've looked at seem too arcane for their own good.

...or perhaps the developers of IoC containers are incapable of writing clear documentation.

...or else both are true to one degree or another.

I don't think the concept of an IoC container is bad. But the implementation has to be both powerful (that is, flexible) enough to be useful in a wide variety of applications, yet simple and easily understood.

It's probably six of one and half a dozen of the other. A real application (not a toy or demo) is bound to be complex, accounting for many corner cases and exceptions-to-the-rules. Either you encapsulate that complexity in imperative code, or else in declarative code. But you have to represent it somewhere.

Using a container is mostly about changing from an imperative/scripted style of initialization and configuration to a declarative one. This may have a few different beneficial effects:

  • Reducing hairball main-program startup routines.
  • Enabling fairly deep deployment-time reconfiguration capabilities.
  • Making dependency-injectable style the path of least resistance for new work.

Of course, there may be difficulties:

  • Code that requires complex startup/shutdown/lifecycle management may not be easily adapted to a container.
  • You will probably have to navigate any personal, process and team culture issues -- but then, that's why you asked...
  • Some of the toolkits are fast becoming heavyweight themselves, encouraging the sort of deep dependency that many DI containers started off as a backlash against.

I think most of the value of an IoC is garnered by using DI. Since you are already doing that, the rest of the benefit is incremental.

The value you get will depend on the type of application you are working on:

  • For multi-tenant, the IoC container can take care of some of the infrastructure code for loading different client resources. When you need a component that is client specific, use a custom selector to do handle the logic and don't worry about it from your client code. You can certainly build this yourself but here's an example of how an IoC can help.

  • With many points of extensibility, the IoC can be used to load components from configuration. This is a common thing to build but tools are provided by the container.

  • If you want to use AOP for some cross-cutting concerns, the IoC provides hooks to intercept method invocations. This is less commonly done ad-hoc on projects but the IoC makes it easier.

I've written functionality like this before but if I need any of these features now I would rather use a pre-built and tested tool if it fits my architecture.

As mentioned by others, you can also centrally configure which classes you want to use. Although this can be a good thing, it comes at a cost of misdirection and complication. The core components for most applications aren't replaced much so the trade-off is a little harder to make.

I use an IoC container and appreciate the functionality but have to admit that I've noticed the trade-off: My code becomes more clear at the class level and less clear at the application level (i.e. visualizing control flow).

Because all the dependencies are clearly visible, it promotes creating components which are loosely coupled and at the same time easily accessible and reusable across the application.

You don't need an IoC container.

But if you're rigorously following a DI pattern, you'll find that having one will remove a ton of redundant, boring code.

That's often the best time to use a library/framework, anyway - when you understand what it's doing and could do it without the library.

In the .NET world AOP isn't too popular, so for DI a framework is your only real option, whether you write one yourself or use another framework.

If you used AOP you can inject when you compile your application, which is more common in Java.

There are many benefits to DI, such as reduced coupling so unit testing is easier, but how will you implement it? Do you want to use reflection to do it yourself?

Related