Dependency Injection

This article provides a short and focused introduction to dependency injection, explains
its advantages, how an inversion-of-control container works and why you’d want to use it.

I’m going to assume that you already have a good understanding of Unit Testing. You should
already understand that Unit Testing is as much about design as it is about finding bugs –
designing something to be testable means making it simple to construct and abstracting its
dependencies on other components to make it simple to isolate.

In the beginning, there was a…

Big Ball of Mud

If you have worked on a complex piece of software, you might envision representing the entire
world in a neat class hierarchy, so the user of your code could write this:

This might appear like a great idea from the user’s point of view, but when you go ahead to
implement this, you will end up with lots of classes depending on each other. Possibly you
want to send notifications around in this hierarchy, eg. when the car corners, the bodywork
calculates the weight distribution from the G forces, notifies the suspension and the
suspension hands over the load to the wheels. If the a wheel bursts, if notifies the
suspension, which notifies the bodywork…

So in the end, you’ll have a monstrosity that possibly takes minutes of manual interaction to
just bring into the state you want to check in the debugger. You’ll lose track of what notifies
what, forget your own ideas about how the design has to fit together, send notifications in
the wrong order or break contract by forgetting a certain notification or side effect.

Testing could have avoided that! If you wanted to write such a thing with Unit Testing in mind,
you’ll either quickly come to the conclusion that “Unit Testing is a thing for purists that you
cannot apply here and will look into later” or, better, discover that your classes are hard to
use, tightly coupled and even harder to test in isolation (remember, Unit Testing is a about
testing the smallest units of your code, not how the entire project works – that’s Integration
Testing!)

Service Hell

Another idea you might come up with is to wrap up your components as services in the style of a
lightweight service oriented infrastructure:

These lightweight services work out well in small scale where the interactions are obvious and
the number of general-purpose services accessed across the board stays low. Unit testing is
done by calling the function with a service provider that contains mocked versions of
the dependencies.

Mocking is the technique of creating place holder objects that implement minimal
functionality to make the tested method believe it’s interacting with the real thing. Mock
objects can contain some assertions as well – in the above example, the mocked
ISuspensionService could make sure that the Unlodge() method has been
called before RemoveTire() is used.

Now as the system grows in complexity, testability quickly goes out of the window because you
can’t see which services a component requires (you’re only handing it a
IServiceProvider) and components typically interact with a dozen or so of services.

So even if the developers were disciplined enough to write concise and well-defined interfaces
all the way, never accessing an implementation directly and clearly defining all dependencies,
the result will eventually be chaos. Testing becomes a nightmare and the design tends to
disintegrate.

Dependency Injection

But fear not, for this problem is one that has been solved already. The solution is called
Dependency Injection, a name that comes from the fact that your component no longer
goes shopping for dependencies in your service container, but gets them handed to it instead:

This solves the problem of testability and clearly communicates the services any given
component needs.

However, you might be asking, why should this be any better than the service container concept?
After all, you’ll end up with a big mess of initialization code handing references all over
the place to wire the components to each other. And where should all the references be stored
to keep the service providers alive when they’re not needed?

Enter…

Inversion of Control

An Inversion of Control container is the missing link to make Dependency Injection
work for you. This container will store your service providers and manage their lifetime.

The Inversion of Control container, named kernel in the example, will automatically
resolve the references for you. When it sees a request for an instance of the
ServiceConsumer class, it will establish that the services IFooService
and IBarService are required and create instances of them as well.

This makes initialization order issues a thing of the past.

Depending on how your container is configured, it could store the service instances and reuse
them when another service requires the IFooService or IBarService.
Or it could create a new instance each time. Or it could provide you with a separate instance
per thread. The point is, you can configure all this without changing the services themselves.

You literally get the best of everything:

Service providers and consumers do not depend on a service framework, so you can write services
and providers in isolation or even use existing classes as service providers without having to
write adapter code for a service framework.

Dependencies of any component can be clearly seen in its constructor. Component initialization
order is no longer something for a guru to figure out but becomes computable and can be resolved
automatically.

Adding a dependency is still as easy as with the service container concept. Just add the
required service to the constructor and the IoC container will provide it to you as well as
adjust the component initialization order if neccessary.

Testability is guaranteed since all dependencies can easily be provided for in unit tests.
Furthermore, the unit tests do not depend on the service framework and can directly work on
the components.

Discourages big consumers interacting with dozens of services. You can write them and it won’t
be any harder than before, but it will be obvious when a component interacts with too many
services.

Dependency management becomes configurable. You can, like in the previous code snippet, set up
dependencies in code with type safety, but you can also set up dependencies from an XML file or
through a plugin system, thereby killing any references between assemblies — all without
having to modify a single line in the service providers or consumers.

Problems like unsolvable initialization order (circular dependencies) can be reported to you
in clear English error messages instead of being found out after rearranging the component
initialization code 10 times and sweating over the debugger display.

Lifetime management of service providers is no longer a source for unnecessary complexity.
The service container can be told whether to keep a service provider as singleton, whether to
create one per thread or whatever else you might require.

In short, all your dependency problems get solved, your service providers can directly be used
like any simple object outside of the service framework, Unit Testing is as easy as it was
without services and you can eliminate many assembly interdependencies.

Writing a service container as described here, one that automatically determines initialization
order, supports configurable object lifetime management and does all the hard work is luckily not
something you need to do. There is already a number of .NET IoC containers out there to choose from:

Ninject

Ninject
is my IoC container of choice because it is very well designed, lightweight and versatile. It
has an intuitive configuration language using a fluent interface and it is one of
the few containers that can be used on the .NET Compact Framework or on the XBox 360 (with XNA).
And it has ninjas.

So without further ado, let’s convert the last pseudo-code snippet into working code that you
can run and fiddle around with:

That’s it. Try to use IoC/DI in one of your projects and find out what you can do with the IoC
container you chose. It will take some time to get used to this style of writing software, but it
will pay off very quickly because it’s faster, cleaner and easier than manually managing your
dependencies. It won’t take long before you get disgusted just looking at tightly coupled code.
Loose coupling rules! =)