There has been some criticism recently of the use of Dependency Injection Containers (DICs) with a lot of people saying that DI != DICs. For example see http://till.klampaeckel.de/blog/archives/154-Dependency-Injection-Containers.html and the comments on it. Whilst I agree with that DI !+ DICs, I do not agree with it being turned into DI good, DICs bad. In this post I want to look a bit more at this. Disclaimer: A lot of what follows is just my opinion and is not particularly intended as a direct response to the above article. It is as much a response to me sounding a bit anti Dependency Injection Containers in this post When Dependency Injection goes Wrong.
DICs as Glorified Registries
A lot of the criticism has been focused on how DICs are essentially just a glorified registry and introduce dependencies on the container. I fully agree that using a DIC in this way, that is requesting services from it, is indeed problematic.
The problematic usage I am mean is code that looks like this:
public function someAmazingAction()
{
$myService = $container->get('my_service');
$myService->DoSomething();
//--
}
So what are the issues with code like this?
Reduced re-usability
We may have removed the direct dependency on whatever my_service is but we have a dependency on the container now. So this code cannot be used without the container, this reduces the re-usability of the code.
Only Slightly Less Tight Coupling
The tight coupling between this class and my_service has been removed, however we have created a coupling between all code that requests my_service from the container as they will receive the same object from the container. We have not created the ability to configure these classes independently of each other.
What is my_service?
In the example code you cannot tell what class my_service is, or more relevantly for using it, what interface(s) it implements. In order to find out you need to consult the configuration of the DIC. Not only can you not tell but your IDE cannot either, so there will be no auto-completion when you are using the service. (I appreciate there are workarounds to this). Additionally nothing enforces the interface of the service so no checking is being done to make sure it is a suitable service before it is put to use.
Unit Testing Difficulties
To write tests for the above code you cannot just mock my_service directly, you either need to use the service container itself to get the mock in or mock the service container and get the mocked container to hand out the mocked service.
This is indeed not Dependency Injection at all in my opinion, you are not injecting the dependencies if you are doing this, you are requesting services from the container, no injection is taking place. Dependency Injection is injecting a class's dependencies through means such as the constructor or a setter method. The container itself is not seen in such classes.
Actual Dependency Injection
So how can we remove the dependency on my_service without running into the above issues? By injecting it directly into the class. In this example it is injected into the constructor:
protected $myService;
public function __construct(MyServiceInterface $myService)
{
$this->myService = $myService;
}
public function someAmazingAction()
{
$this->myService->DoSomething();
//--
}
So our code now has no direct dependencies on another object, just a requirement that an object implementing the MyServiceInterface interface is passed into the constructor. So there is no tight coupling between the class and a particular implementation of the interface. We have also avoided any coupling with the container.
Other classes which require an implementation of MyServiceInterface can receive the same or a different implementation now so there is no coupling with them. You and your IDE now know you are working with a MyServiceInterface implementation so you will get auto-completion. To test this class we can now just create a mock implementation of the interface and inject it in directly with no need to involve the container.
A big advantage to using DI in this way throughout your code, is that it fully separates the configuration from the rest of the code. This means that you can wire up the classes differently for different applications easily. None of the code outside of the parts doing the wiring needs to be aware of the particular configuration. There is no need for messy code choosing which version of a class to use. If you need different functionality for a different app then you can just drop in a class with the new functionality without needing to touch the existing code.
There is a learning curve to starting to code in this way, as well as getting used to having to create the DI configuration instead of creating classes directly in the code, but its one that is well worth it.
Where does this leave DICs?
So does this mean that DICs are bad full stop? No quite the opposite. If you do DI properly and use constructor and/or setter injection (and possibly property injection) then in a large application DICs offer many advantages in wiring up the application.
For very small apps and when writing tests it is easy enough to just create the classes and pass them in as dependencies. In a large application with a complicated object graph this becomes a difficult task. This is where a DIC comes into its own. I am mainly going to concentrate on the Symfony2 service container when looking at the advantages of using a DIC for this large scale wiring, as it is the one I am most familiar with.
The first advantage is that the Symfony2 container allows you to configure services using YAML, XML or PHP and then takes care of the actual creation of the objects. This means that you can write much more readable configurations. For examples of configuring Dependency Injection in Symfony2 using XML see my post: Symfony2: Dependency Injection Types
Another advantage is that you do not have to configure everything in one place. In Symfony2 the code is separated into bundles, both the core code and third party code is all in this bundle form. Each bundle contains its own services configuration files so only the DI configuration for the bundles actually being used is read into the DIC. Trying to create such a complex wiring in one place without the help of a DIC would be a mammoth task.
The DIC also carries out other tasks to aid in performance. This is particularly important when doing large scale DI in PHP as these are not long running services as with Java but created on each request. One aspect of this is caching of the confguration once it has been "compiled" from the XML or YAML config files. Another is the use of proxy classes for lazy loading, meaning that the full object graph is not created every time but only when services are actually used. All this happens behind the scenes but is completely separated from the code that is wired together, which itself is unaware of the way in which it is wired up.
Conclusion
So is it true to say that DI != DICs, well yes sort of. More accurate would be to say that DI, possibly using a DIC for configuration != requesting services directly from a DIC being used as a glorified registry. Unfortunately that's not quite as snappy.