Yesterday’s post in this unexpected series on Dependency Injection vs Service Locator generated a lot of excellent and productive feedback in Twitter, on the PHP-FIG mailing list, and in the comments.
In that post, I opined that there was a significant difference between how Service Locator containers and Dependency Injection containers are implemented. This came from my sense that because they have different names they must be different things, and so I had been trying to find a way to discern the difference by looking at the implementations.
They’re Different Names For The Same Thing …
As the disucussion progressed, it became more clear to me that there really is no significant difference in how Dependency Injection containers and Service Locator containers are written. They are both Inversion of Control (IOC) containers, and are not distinguishable by their code, API, features, etc. (although some may have more or fewer features than others).
As such, the terms Dependency Injection and Service Locator appear to be interchangeable in the sense that a container is a container is a container. The difference in naming comes from how the container is used, not how the container is implemented:
When the container is used outside a non-Factory object, you are using the container for Depedency Injection.
When the container is used inside a non-Factory object, you are using the container as a Service Locator.
(Side note: Using the container inside a Factory object does not seem to be a case of either Dependency Injection or Service Locator specifically; it’s just a generic container usage at that point. This stems from the idea that object creation should be separated from every other type of object operation; that is, a class should either create an object, or it should operate on objects, not both. Factory, Builder, etc. classes are thereforce special cases.)
… But The Names Still Indicate A Difference
This still leaves me with a trio of related issues: usage, naming, and intent.
I am big on trying to use the right names for things. I think it’s important for clear, consistent communcation that when Developer A uses a word or phrase to describe something, Developer B should be able to understand what Developer A is getting at. The need for common understanding should be so obvious as to not require pointing out. With that in mind …
If an author creates a “Dependency Injection” container but his intent is for it to be injected into non-Factory objects so the objects can retrieve their dependencies, he has named it incorrectly. He can correctly call it a “Container” or “IocContainer” in general, or a “Locator” or “Service Locator” in specific, but his intent and usage are clearly at odds with his naming. (Closures and functions count here, too. Injecting a “Dependency Injection” container into a closure or function is a Service Locator usage of that container.)
Similarly, if a user uses something labeled as a “Dependency Injection” container by injecting it into his non-Factory objects so they can retrieve their dependencies, his use is at odds with the naming and intent of the container. He is using it as a Service Locator.
Conversely, if an author creates a “Service Locator” that is used only in Factory objects, and never in other kinds of objects, the name is at odds with the intent and usage as well. That’s a “Dependency Injection” container, or more generically a “Container” or “IocContainer”.
Finally, if a user uses something labeled as a “Service Locator” and never injects it into his non-Factory objects, his use is at odds with the naming and intent of the container. He’s using it for Dependency Injection.
As a result of all this, I am left with the conclusion that container authors should be very careful about their naming, and that container users should be disciplined about their usage, both based on the intent indicated by the container’s name:
If the author intends the container to be injected into non-Factory objects, including closures and functions, call it “Locator” or “Service Locator”. Users should honor that intent.
If the intent is that the container never be injected into non-Factory objects, including closures and functions, call it “DI” or “Dependency Injection”. Users should honor that intent.
If the author has mixed intent, call it a generic “Container” or “IocContainer”. Users can mix Dependency Injection and Service Locator in Factory and non-Factory objects and closures.
Again, the point here is to make sure that other people know what we’re talking about when we use these terms. Using the wrong names leads to confusion regarding usage and intent.
Are you overwhelmed by a legacy PHP codebase? Do you want to improve it, but don’t know where to start? My upcoming book, Modernizing Legacy Applications in PHP, will lead you step-by-step through a series of small, incremental changes that will dramatically improve the quality of your legacy codebase. Subscribe to the mailing list below for pre-release chapters and more!