Inversion of Control
Dependency injection is a concrete form of a wider concept: inversion of control, a design principle extensively used by frameworks, according to which the custom part of your code receives the flow of control from the framework (instead being the custom code the one “in charge”, the one calling some libraries).
The main idea behind dependency injection is to decouple objects, so that the client code doesn’t need to be changed whenever one of its dependencies needs to be changed. There are other ways of decoupling, such as using a service locator. In both cases (with dependency injection and with a service locator), the client code is independent from the concrete implementation of the dependency. The difference is that, with a service locator, the application asks for that implementation, whereas with dependency injection there is no such explicit request.
Dependency Inversion Principle
Put in different words, dependency injection supports the dependency inversion principle, (one of the “first five principles” of SOLID object-oriented programming introduced by Robert C. Martin in the early 2000’s), according to which the client delegates the responsibility of providing its dependencies to an external code.
Implementing Dependency Injection
There are three ways of implementing dependency injection: constructor injection, setter injection and interface injection. A good explanation on the differences between those concepts can be found on Martin Fowler’s article from 2004, Inversion of Control Containers and the Dependency Injection Pattern.
When you need to manage lots of different objects with lots of dependencies, the need for a dependency injection container soon arises. A dependency injection container is simply an object that knows how to instantiate and configure dependencies.
Dependency Injection with Symfony’s Service Container
Some early dependency injection containers were implemented in Java’s Spring framework and PicoContainer. When the Symfony framework overcame its great evolution from Symfony to Symfony2, it embraced dependency injection, which is managed by Symfony’s Service Container.
Dependency Injection – from difficult to indispensable
As many other practices that make your life easier and your code better, once you are used to dependency injection it becomes something you cannot live without. It is even difficult to remember how you used to do things before mastering that concept. But the truth is that it can be difficult to grasp at the beginning, despite its actual simplicity.
This is something you realize when trying to help or collaborate with external teams that are immersed in rewriting a legacy code – which might be originally written in plain PHP, or some early framework. Dependency injection is one of the things that makes you feel constrained at the beginning, as if you were wearing some sort of armor that makes free movement impossible. Of course, with time you realize that it is not necessarily a disadvantage, once you know how to deal with that new environment. But at the beginning, you can only see the things that you cannot do. Sooner or later you realize that those things cannot be done for a reason. And that your code has actually become much better: more reusable, testable, and maintainable.
Benefitting Your Code
Also, as many other powerful concepts, you can just learn how to use it – following some recipe – or you can understand why do you use it and what benefits it brings to your code. The latter is always a better choice, that sometimes – as it is the case with dependency injection – does not even require much effort. In this post we have tried to clarify those foundations. In the session “Understanding Dependency Injection in Symfony” at the International PHP Conference we will see in depth the concrete implementation of dependency injection in Symfony, showing how to take full profit of it.