During my tenure as a seasoned, and tenderized, PHP developer I have used many design patterns: adapters, factories, data mappers, facades, etc. The most recent one that I have been working with is Dependency Injection. Inversion of Control is not a new idea, at least not in the programming world, but in the PHP world it seems to have taken us by storm in recent years. Every framework will often have a Dependency Injector built in, or offer it as a component for use. It is the hip thing to do in PHP, but I believe we need to take a step back and evaluate it for what we are really trying to achieve. That is reducing the tight coupling that our objects may have. I view it as removing the new-able’s from our objects code, and handing the object creation over to something else to deal with. Read further on how I believe DI can become an anti-pattern.
Tony Marston, Nat Pryce, Jordan Zimmerman all write about how Dependency Injection is wrong and makes for harder to maintain code. Martin Fowler on the other hand compares service locator’s and dependency injector’s in such a way that they are essentially the same thing, just with differet names. It took me several hours to unwind the conflicts that I had about what these authors had written. For the last several years, like the rest of the PHP community, I’ve read about how Dependency Injection was the best practice. It seemed like it was required in order to be a good developer, promote unit testing, and that service locator’s were bad because they are basically singletons and/or registries.
In a work project I started several years ago we decided to use configuration to control our dependencies, instead of letting the objects instantiate what it needs. It started it’s life as a service locator and after several iterations has become like most, if not all, of the other dependency injector implementations available (minus lazy loading). I’ve come to the conclusion that at the heart of EVERY dependency injector there is a service locator. The DI that Symfony 2, ZF2, Aura.DI, and everyone else uses, are basically using a service locator internally to some degree to locate what it needs. This doesn’t mean you should just switch to using a service locator for everything.
In that same project I spoke of earlier there is currently one service being injected with six (6) services and ten (10) mappers. I’ll admit that the service is doing a lot, it is essentially an aggregate root (the closest terminology I can use for what it does). Not all of the injected objects are used on every request, but since we don’t know what is needed until the request is over it gets them all. Moving those mappers into a very specific Service Locator would reduce the needed injection object to one and keep the dependency instantiation outside of the service. As mentioned earlier I believe that Inversion of Control is meant to remove the object creation and life cycle management outside of the object that is using that dependency.
I believe that both dependency injector’s and service locator’s can co-exist, and both have their usages. Just like any pattern it can become an anti-pattern when overused, and used incorrectly. If you are never going to be injecting a different dependencies why are you using a dependency injector? If you say because it makes unit testing easier, think about it a little harder, I did! Several observations about unit testing the objects we use:
- I often write unit tests without ever using the dependency injection container, I use dependency injection but I inject them manually and directly!
- The service locator can be injected with what you need in order to perform tests too, so therefore the unit under test is still testable.
My belief that dependency injection was going to be the cure all for my work project seems to have backfired. We don’t ever change dependencies dynamically, and have only change dependencies once in the three years of the project. I never use the DI container for testing and my code seems to be a more complex as now I have to manage the dependencies in multiple places.
All that being said, code author’s writing libraries for others to use in their projects, “be kind interface”, to paraphrase the movies name. It makes swapping out the dependencies easier, whether consumers choose to use a dependency injector or a service locator.