Unless you build your object graphs by poisoning your constructors with tons of "new" operators (which is a cardinal sin), you know that dependency injection and service locators are currently the two most popular approaches to connecting sets of related objects.
In the PHP world, dependency injection (AKA inversion of control) is quickly being adopted, due to its relatively simple implementation and the benefits that it brings to the table, such as the construction of easily testable classes. But in truth, the utilization of a service locator deserves a close analysis as well. This approach can be quite helpful in those use cases where the injection of collaborators is not a viable option.
In line with this idea, in the first part of this series I went through the development of a basic registry library. It used a single service locator to provide a client class with its dependency -- in this case, a naive array-based registry. Moreover, to help you see the differences between using dependency injection and a service locator, I deployed the library by using each approach individually (more hard work for me, but hopefully in pursuit of a just cause).
It's probable that you're thinking that these approaches are mutually exclusive. They really aren't. In fact, it's possible to inject a service locator either through a constructor or a setter and get the dependencies of a given object. In most cases, this method is used when those dependencies have a shorter life cycle than the object that needs them. In a situation like this, the locator is a plain factory or a builder, which spawns collaborators on request.
Since the scenario just described is pretty common in modern web application development, in this second tutorial I'm going to modify the sample registry library previously developed. This time, it will combine the functionality of dependency injection and a service locator within the same client class.
Are you ready to see how this will be accomplished? Then start reading!
Recap: building a simple service locator
Just in case you haven't read the previous article, where I explained how to implement a basic service locator, below I included the source classes (and the interface) corresponding to this example. As noted in the introduction, the locator was utilized as part of a registry library, which permitted you to save and fetch data from a plain array.
Having said that, here's the interface that defines the methods of an abstract registry:
Understanding the contract that the "Registrable" interface establishes is a process that doesn't bear any further discussion. So, the next step is to show the definition of a concrete registry, which in this case stores and retrieves data from a protected array. This one is an implementer of the previous interface, and its source code is as follows:
class ArrayRegistry implements Registrable
So far, so good. With this concrete array-based registry up and running, I could create a script that puts it to work directly. But, since I want to abstract the storage and retrieval of data, I'm going to hide the registry from the outside and use a client class instead.
If you're wondering how this client class obtains an instance of the array registry, the following service locator should answer your question quickly. Check it out:
Effectively, the above locator is a static factory responsible for creating the pertinent array-based registry. With this element added to this sample library, building a client that consumes the registry is as simple as defining the following class:
Even though the implementation of the above "Registry" class is somewhat primitive, it shows in a nutshell the role of a service locator. In this case, the class appeals to the locator to get its collaborator, via a call to its static "getArrayRegistry()" method.
Of course, the complexities of this internal process are shielded from client code. This is demonstrated by the following code snippet (the autoloader's definition has been omitted for the sake of brevity, but you can see it here). Look at it:
// 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
I don't want to make Mary Wilson angry, but the way that previous script manipulates her data is in this case is completely irrelevant. What's really important here is that the "Registry" class gets its dependency through a service locator, which is invoked statically.
But, before I hear you insisting that the same functionality can be achieved with dependency injection, first take a deep breath and calm down. If you analyze the "Registry" and "RegistryLocator" classes, you'll realize that it's possible to modify their respective definitions and make the former ask for the latter in the constructor.
Does this sound confusing? Well, fear not, as this approach is pretty common nowadays. It consists of injecting a service locator, instead of invoking it statically. Therefore, in the next section I'm going to amend the source code of the aforementioned classes, so the locator can be injected into the client registry's internals.
To see how this will be done, move ahead and keep reading.
blog comments powered by Disqus