In order to make the previous "Registry" class get its dependency via a service locator, it's necessary to introduce two changes into the whole registry library. The first one, logically, is to define the service locator (or a registry locator, to be more precise) and the second one is ... yes, to modify the class itself.
Since the definitions corresponding to the "Registrable" interface and its implementer "ArrayRegistry" will remain exactly the same, I'm not going to list them again, to keep the sample codes uncluttered.
With that said, here's the implementation of the service locator, which not surprisingly is called "RegistryLocator":
That was easy to code and read, wasn't it? As seen above, this service locator is nothing but a static factory that spawns instances of the "ArrayRegistry" class and returns them to client code via its "getArrayRegistry()" method. Nothing less, nothing more.
With this service locator already defined, the last step is to tweak the source code of the "Registry" client class, so it can obtain its dependency through the locator. Here's the amended version of this class:
There you have it. Now the above client class fetches its dependency (in this case a single object, but it might be multiple ones) by calling the static factory method of the service locator. Of course, if you're anything like me at this point, some alarm bells should be ringing off in your head. But why?
First off, the class is no longer capable of working with multiple registry back-ends, unless a new factory method is added to the locator. And last but not least, testing it is much harder.
Aside from demonstrating how to implement a simple service locator in PHP, this trivial example also shows the pros and cons of using this approach. On one hand, the class using the locator effectively hides from the outside how it obtains its dependency; on the other one, though, it sacrifices flexibility and testability. That's why in many cases a service locator is considered a degradation of dependency injection.
But before you express your own judgment, first it's necessary to create an example that puts the registry locator to work, so you can see if it really functions as intended.
This will be done in the following segment. So go ahead and read the next few lines.
The service locator in action
As I stated previously, below I coded an example that demonstrates how to use the "Registry" class, now that it gets its dependency through the array locator. Take a look at the following code fragment:
// example using a service locator
// include the autoloader
// create an instance of the registry and save some data in it
// get the data from the registry
Although rather contrived, this example shows pretty clearly the impact produced by the implementation of the previous service locator. Effectively, once an instance of the "Registry" class is created, it's used to store and retrieve some data from the array registry, even though this dependency has been provided "behind the scenes."
This is in fact a double-edged sword: from the client code's point of view, using the registry is slightly simpler, as there's no need to perform any constructor or setter dependency injection. However, from a programmer's standpoint, testing the registry and even figuring out how the registry gets its dependency is definitely more difficult.
In my opinion, dependency injection is the way to go when building object graphs. This doesn't mean, however, that a service locator should be abandoned in a dark, isolated corner and condemned for its evil actions. As I said before, there's a number of cases where it can be useful, particularly when combined when inversion of control.
But I'm getting ahead of myself, since that is the topic that I plan to cover in the next installment.
In this first chapter of the series, I provided you with a humble introduction to the key concepts that surround the definition of a service locator, and also showed you how to use this approach during the development of a basic registry library. As you just saw, there are some serious drawbacks that make a service locator more inefficient than dependency injection, especially when it comes to constructing testable object graphs.
It's possible, though, to combine the best of both worlds and inject a service locator. This method is quite interesting, and deserves deep analysis. Therefore, in the upcoming tutorial I'm going to modify some of the source classes that comprise the earlier registry library, so it can use an injected registry locator.
Don't miss the next part!
blog comments powered by Disqus