Design Pattern #2: Dependency Injection

A very useful pattern to keep your application clean and enable unit testing

In the previous design pattern article, I went over the Value Object pattern.

This time I will talk about how and why you should use dependency injection. Personally, this may be the design pattern that I use the most, and it's generally very common so if you don't know it, I strongly encourage you to learn about it!

I'm personally a bit annoyed about articles that uses cats and dogs, or wheels and cars as examples for this kind of stuff. It doesn't reflect what people usually work on. Therefore, I will instead use real life examples, mostly taken from this blog's source code.

What's dependency injection and how do I use it?

Let's say you have a class, and that class has some dependencies on other classes.

In our case, I have a service class to retrieve a blog post (such as the one you're reading right now) for a given ID, and its dependency is a doctrine repository that is in charge of connecting to the database and retrieve that post.

Well, one naive way to have your main class using your dependencies might be to declare its dependency inside it.

That would work! You would be able to use your service, and it definitely has an instance of the repository created in its constructor. However, the constructor is now polluted with all the logic of how to create the repository.

Isn't that cleaner? Now, of course you'll still need to create the repository somewhere beforehand to pass it into the service.

However, it makes your code way easier to read. And there are more advantages than just code readability too...

Why should I use this?

Centralize your instantiations

One reason you may want to use dependency injection is to have your instantiations written in one place only. Using our previous example, what would happen if I added another service that needs the PostRepository?

If the repository was instantiated in the services constructor as we did first, the new service would need to do the same! It means we would duplicate that ugly code to retrieve the repository from the doctrine entity manager.

However, using dependency injection, we can simply pass the same instance of the repository to both services, and avoid having duplicated code.

Having no duplication of that code means that if we ever need to change how that repository is created, we can change it in one place only.

Now, if you don't know how to handle these instantiations, I suggest having a look at containers, such as this one.

Unit testing

Another huge advantage to dependency injection is that it allows us to unit test our code easily.

Indeed, if you wanted to test the behaviour of the getPost method on our service, you would need to have a database setup for the test, and therefore you would test both the service and its dependency (the repository). While this isn't necessarily a bad idea, it can be quite difficult and slow. Furthermore, it would be closer to an integration test than a unit test.