Friday, February 22, 2008

IoC Containers generally, Castle Windsor specifically

First I’ll have a go at reinventing the wheel by trying to give my own definition of an IoC Container

An Inversion of Control (IoC) Container is a non-invasive configurable intelligent factory-component

Splitting this definition in parts, we get
  • Factory, because it is responsible for creating objects for you.

  • Intelligent, because it understands what dependencies you have, and create them for you recursively.

  • Configurable, because you can configure the usage through code or configuration files.

  • Non-invasive, because the objects used doesn’t need to know about the container.

An IoC Container really makes the Dependency Injection pattern a lot better. Why? I’ll look more into that throughout this post.

My previous post is a foundation for this one, as it explains the Dependency Injection pattern, and gives it a place in the world. I’ll use no time on that here. This post exists to build on that content, and show what more benefits you can achieve by using the dependency injection pattern with an IoC Container. This isn’t meant to be a comprehensive introduction to IoC Containers or Castle Windsor, but should give you a good idea of what it can do for you. Once you’ve read it, just continue on with what I’ve added in “References and further reading”.

I’ll use a simple example to help explain how it works. We have a client, which uses a service, which again depends on IStorageObject, which have two specific implementations. This is not meant as a real world example, but bear with me. The dependencies and sample implementation are shown below:












My previous post shows how we can handle this manually, and the pros and cons with the different solutions. We would like to get an instance of client, but we need to handle all the dependencies it has as well. If we have to write this manually, it will turn out as something like this:



This doesn’t look too bad, and the dependency injection approach is often better than a lot of alternatives. But think of it. As soon as this starts expanding, I can bet you that you’ll have a mouthful.

Now to do the same with an IoC Container, you can typically write:



For the container to create Client, it needs to create an instance of Client, but also an instance of both IService and IStorageObject. The container will understand that it needs to create both, and does so depending on how you have configured it. More on that later.

What if we add another dependency to the service implementation? The manual code would have to change to something like this (Each and every place service is created in the code. Resharper could help you quite a bit, but it’s far from a solution to the real issue.):



Already guessed how it would look with the container approach?



You are right, just the same :)


This has a number of advantages:
  • Easy to swap the implementation of an interface - just change the configuration.

  • If you find that Client suddenly needs another dependency, you don’t have to rewrite any of the Client creation code.

  • Removes explicit dependency-handling throughout the system

  • Single point to find out how objects are instantiated and connected

  • Easy to begin using Client in other places. (Less boilerplate code to write)

  • Additional benefits of tunneling dependencies through one source: Decorators, Interceptors, Proxies, as well as handling object lifestyle. Have a look here for a very good example.

  • See my previous post for more on the general benefits of dependency injection.

I am going to have a look at an IoC Container called Castle Windsor now. There are many other alternatives as well. I can’t speak too much about the others as I only have real life experience with this. If we talk only about the general dependency injection problem/solutions, the different projects have a lot of similarities, with some differences in support for config vs code, need for attributes etc.


Configuration and use - with XML

You can use a separate configuration file, or add the castle definitions to your app/web.config.



The configuration above states:
  • The string identifier of our only component is “storageobject”

  • The service (or interface) the container will create (if any - you don't need to specify one), is IStorageObject, from TheNamespace namespace

  • The specific implementation of this interface is DatabaseObject.

In practice, this means that if the container is ever asked for an implementation of “storageobject” or the IStorageObject interface, directly or through resolving dependencies, it will return an instance of DatabaseObject..



This code simply shows how the container would be started with this approach.


Configuration and use - with code

You can define everything in code:




The pros and cons of using XML versus code is that you can do changes without recompiling with the XML approach, while it is type safe when you specify it in code. Use whatever is best for your situation.


Lifestyle

Advanced handling of the lifestyle of objects has never been so easy. By adding a keyword, you can specify how your object lifestyle will be:



The previous configuration updated with specification of lifestyle:




Facilities

Adds integration with other tools
  • NHibernate

  • ActiveRecord

  • WCF integration

  • Remoting

  • Logging

  • Startable

  • Custom

  • +++

Other

Castle has a number of other possibilities as well, like specifying what to do when objects are created and destroyed (Commision and Decomission).

Note

The last couple of points feels kind of pointless without giving them much extra attention, but I'll leave that to other people who have described it much better. A good starting point would be this or this.

References and further reading

Inversion of Control and Dependency Injection with Castle Windsor Container - Part I (Simone Busoli)

Inversion of Control and Dependency Inject: Working with Windsor Container (Ayende)

A not so interesting post, with much more interesting comments.

Castle Windsor tutorials (BitterCoder's Wiki)

Inversion of Control Containers and the Dependency Injection pattern (Martin Fowler)

Castle Project

Conclusive thoughts

Although a short introduction, I hope I have captured enough information to get you interested. I’ve been using Castle for quite some time on my last project, and I am very happy with the results. I advise you to give it a try!

_

2 comments:

Lars Andreas Ek said...

Good post!

I was a bit puzzled about the "TheNamespace.ClassName, TheNamespace" for qualifying the type. Isn't is supposed to be "TheNamespace.Classname, AssemblyName"?

Rune Sundling said...

You're of course correct. Apparently been too sloppy.. Thanks for correcting me