Let's face it: it takes years to master the complexities of good object-oriented programming, even when using an easy-going language like PHP. Mastering the paradigm demands a solid background in its major pillars, such as Inheritance, Polymorphism, Composition/Aggregation (feel free to add your own topic to the list). It also calls for tackling an unavoidable fact of its intrinsic nature... yes, sooner or later, objects need to be created! Even though the subject appears trivial at first glance, class instantiation is a challenging process that requires a lot more of planning than many people think. The adoption of good programming habits, coupled with the use of well-known principles, like The Law of Demeter (http://en.wikipedia.org/wiki/Law_of_Demeter) and separation of concerns (AKA The Single Responsibility principle: http://en.wikipedia.org/wiki/Single_responsibility_principle) very often lead to the development of applications that are composed of a huge number of objects with limited responsibilities. These objects need to be properly wired up, so they can interact with each other as intended. In such a scenario, many programmers ask themselves what the best approach to take is when building object graphs, regardless of how complex they can become. Well, currently there are two streams of thought that clearly stand out from the crowd. These are the implementation of dependency injection (DI) and the use of service locators. While the former has certainly gained a lot of popularity in the last few years, the latter still remains rather overlooked, especially in the PHP field. Contrary to DI, where objects ask for their collaborators, either through their constructors or setters, service locators act like mediators, responsible for providing those collaborators on demand to the objects in question. To be frank, there are some good reasons that justify this "deliberated" indifference to service locators. The first one is that they're hard to test (unlike DI, where the use of mocks and stubs makes it easy to test classes in isolation). The second one is that, quite frequently, a service locator introduces a strong coupling with the object that requires its services. And we all know that coupling is a clear sign of code that stinks, right? However, there are certain situations in which a service locator may be pretty helpful, especially when combined with the benefits provided by DI. Therefore, in this article series I'm going to set up some code samples to show you how to implement several service locators in PHP. This way, you'll understand their underlying logic, and decide whether or not they fit your needs. Ready to begin this hopefully educational journey? Then let's go! Creating object graphs using dependency injection: developing an extensible registry Since my plan here is to illustrate how to construct a few basic service locators, the first example that I'm going to create will develop a simple registry library. The library will rely on the functionality of dependency injection to switch to different registry back-ends at run time. Once you grasp how the library does its thing, I'm going to refactor its implementation, so it can use a service locator instead. Having clarified that point, the first element of the library is an interface called "Registrable." It defines the contract that must be fulfilled by all of the registry back-ends. Here's the interface: (Registry/Registrable.php) <?php namespace Registry; interface Registrable As you can see, all that this interface does is define an abstract registry. This process is not especially interesting. So it's time to move on and create a concrete registry, which naturally must implement the interface. The one shown below does precisely that. Check it out: (Registry/ArrayRegistry.php) <?php namespace Registry; class ArrayRegistry implements Registrable As its name suggests, the above "ArrayRegistry" class is a super simple registry. It permits you to save and retrieve data from a protected array. Since this class is effectively an implementer of the previous "Registrable" interface, it's possible to create a client that injects an instance of it into its internals for further use. The following client class is capable of consuming the previous registry, or any other that implements the "Registrable" interface. Look at it, please: (Registry/Registry.php) <?php namespace Registry; class Registry /** At this point things are becoming a bit more exciting, as I managed to create a client class which can inject different registry back-ends, including the array-based one defined a moment ago. Having achieved that, the next logical step is to set up a script that shows how to use this basic registry library. Since I want to avoid using multiple PHP requires, I'm going to utilize a simple autoloader. It's the same one that you've probably seen in previous articles published here at the Developer Shed network. For the sake of completeness, I included its definition below. Of course, you're free to skip over it if you want to: (Autoloader.php) <?php class Autoloader
(AutoloaderException.php) <?php class AutoloaderException extends Exception{} With the earlier autoloader already up and running, the only thing left to do is write the aforementioned script, which will show how to work with the previous registry classes. Here is the snippet that performs this task: <?php // example using dependency injection // include the autoloader // create an instance of the registry and save some data in it // get the data from the registry Mission accomplished, at least for now. Thanks to the combined functionality of the earlier classes, storing and fetching data from the array-based registry is actually a breeze. Beyond the simplicity of this example, you should focus your attention on how the "Registry" class works. Yes, to do its business the class "asks for" a registrable object in its constructor, which in this case turns out to be the pertinent array registry. Simply put, this means that the class's collaborator is injected. Period. While this approach is very efficient (and easily testable, by the way), what would happen if the "Registry" class obtained its collaborator by means of a mediator object, instead of using constructor injection? Well, in a case like this, in programming jargon this mediator would be called a "service locator" and would be responsible for factoring all of the dependencies required by the class. So, now that you have a vague idea of what a service locator is, in the next section I'm going to modify the previous example. This time, it will use a service locator to provide the "Registry" class with its dependency. To learn the full details of this process, jump ahead and keep reading.
blog comments powered by Disqus |
|
|
|
|
|
|
|