There are two terms that have been loudly spoken for a while now IoC (Inversion of Control) and DI (Dependency Injection). In this post, I’ll try to sketch a picture of what these terms actually mean.
IoC is an abstract principle regarding software construction that has been invented to fight the problem of tight coupling. Let’s take the following code sample:
This looks like a perfectly valid piece code (and it was a while ago) but there is one problem with it. The class Repository cannot exist without the Database class, moreover a Repository object will always use the Database class to persist it’s data. It’s not possible to pull these two apart – they’re tightly coupled.
Why is this bad? Well, what if you want to test this code? “Easy” you say, just change the connection string in the configuration file to point to a test database and there, problem solved.
But what if you need to test more operations, and one is excluding the other or they both rely on the same database state which is altered after you run a test? “Restore a db before each test” – sure, that’s possible, but who’d like to do that?
So what can we do to make this better?
Ok, that’s a little better. We’ve introduced an interface which is an abstraction for the database itself, so now if we don’t want to use the Database class, we can just exchange it with something else that implements IDatabase – that’s good, but how?
Well, there are several ideas:
This is a bit better, because we moved the instantiation outside of the Repository class, we control it through the factory. But if we want to change the object, we still need to modify the DatabaseFactory class
This is even better than the previous example. Not only we’ve moved the instantiation outside of the Repository class, but now we’ve made it possible to change the used object at runtime. Our tests would just need to register an IDatabase implementation before they run by calling
which could be a static list or something that suits our needs – we’re making progress!
But...imagine you don’t see the implementation of the Repository class, how can you tell it needs an IDatabase object? You can’t. So what we’ve done here, was to improve the code, but destroyed the abstraction by hiding class dependencies. It’s all OK if we’re implementing everything ourselves but that’s not going to last forever.
So what have we done here? We’ve just staid “ok, I’m a repository, I get stuff for you, but I need to have a place to get it from and I can’t live without it” – this pattern is called constructor dependency injection. It addresses both of the previously mentioned issues – the instantiation of the database object is moved outside of the Repository class and, we’re not hiding our dependencies any more.
Our test code is very clean now, in order to test the Repository class we could write
Nice, but… what if I need a default constructor? (this is the case e.g Controllers in asp.net MVC), well, we can modify this a bit:
Now we have a default constructor, and both our issues are resolved. The client would need to inject the database object just like earlier, just not through a constructor, but a bit later.
The difference? well, now our repository sais “ok, I’m a repository, I get stuff for you, but I need to have a place to get it from, but you can give it to me anytime, as long as it’s before asking to get anything” – this pattern is called property dependency injection. But there is one problem here, what if the client forgets to provide the database object? Well, easy:
Now if someone forgets, we get a runtime error, which we’ll be able to detect with our unit tests, right? Wrong. The unit test is the client, we can remember to supply the database object in our tests, and forget in our application, and we’ll find out about it when it crashes.
So why the change? Is it a step back? The constructor injection didn’t have this problem, why bother? Well, sometimes, you just don’t have a choice. Both injection types are valid, both have their preferred usage scenarios and none of them is a silver bullet to kill the tight coupling problem, but both are a lot better than what we started with.
There are of course some downfalls – what if we have a class that requires a class that requires a class? There’s also the subject of DI Containers. We’ll cover both in a later post.
So what have we learned?
Inversion of Control is an approach, an abstract concept that allows us to create loosely coupled classes, which in turn gives us better maintainability, testability and makes us feel warm inside.
Dependency Injection is a pattern used to implement the IoC approach, not the only one, but at the moment, the best solution.