Phil Bennett

Software Developer. Built league/route and league/container. I'm like Tom Cruise in that film Cuisine.

Dependency injection, service location and inversion of control

December 22, 2013 in #php

(The Good, The Bad and The Hard to Summarise)

I was recently asked if I would be interested in contributing a section on Dependency Injection to PHP The Right Way. Obviously I was more than happy to do so and the pull request was merged in a couple of weeks ago.

I planned to write a post following that with a little more in-depth explanation and a few real world examples, however, Paul Jones started a series of essays that I thought would alleviate the need for a post from me, so I decided not to continue with it.

The essays provide a great insight in to the subject, but one of them in particular, Quicker, Easier, More Seductive: How To Tell A DI Container From A Service Locator, I think could cause confusion for developers new to the subject.

Edit: Paul has since posted another essay in the hope to clear up any confusion. Quicker, Easier, More Seductive: Names, Usage, and Intent.

In this post I hope to give a clear description of the differences between these three design patterns and the tools used to implement them. I will also be providing examples of how the frameworks and libraries that we use (past and present) have handled the problems that these patterns aim to solve.

Inversion of Control

As suggested in the title of this post, Inversion of Control is a difficult pattern to sum up in a way that everybody reading would identify. It is a broad pattern that parents several sub patterns. Taking these sub patterns in to consideration (two of which I will explain later), I think the concept can be summed up as follows.

Inversion of Control provides a method of removing the control of creating dependencies from our classes and moves that control to another part of the system.

This can be achieved in several ways, most of which, until recently we as application developers have not really considered. Our framework of choice would handle the creation of our application specific objects and provide several convenience methods (through inheritance) for the creation and use of dependencies.

Most frameworks before the adoption of PHP 5.3 would require that your controllers would extend a base (or abstract) controller, which in turn would extend or have access to what we would lovingly refer to as a super object. This super object would provide the convenience methods I described earlier.

Codeigniter was (and to a point, unfortunately, still is) a ridiculously popular framework, so let's take a look at how they did it.

When you called $this->load->model('Model') (a convenience method defined in the loader which is created in the base controller) Codeigniter would request that model from the loader that would in turn, create and instantiate the model and return it for use in the contoller.

Here the control of creating and instantiating dependencies has been inverted to the loader, which is an early implementation of the Service Locator Pattern.

Service Location

The Service Locator pattern is one of the sub patterns of Inversion of Control. It describes obtaining a service through the use of a strong abstraction layer. This abstraction layer will normally be a central registry that when called upon will return the described object for use.

As displayed in the above example, the registry in question would be the Loader which would create and instantiate our dependencies.

There are several problems with this that have lead to it being considered an anti pattern.

If we want to test a unit of code in isolation, it is just as difficult when using service location as it would be by instantiating dependencies directly. If our unit of code creates and executes code on a dependency, our test results can quite easily be skewed with it being much harder to locate a point of failure.

The early enterprise frameworks such as Zend Framework 1 addressed this by eradicating the use of service location at application level. They would still provide convenience getter methods for accessing some dependencies, but instead would instantiate the dependency directly if there was no instance available at the time of the call. With testing in mind they would also provide a setter method that would be able to optionally replace the instance that was being returned by the getter, possibly with a mock object.

Here are two examples of how this was done in Zend Framework 1, one from the application perspective and one from the testing perspective.

Application

Testing

These two examples show an implementation of the Dependency Injection pattern, meaning that in testing we can mock the Request object and inject that for use rather than the real implementation, this way our tests can control dependencies and test our code in true isolation.

Dependency Injection

Dependency Injection, another sub pattern of Inversion of Control is fast becoming the de facto solution to the problem. It describes providing an object with it's dependencies through constructor arguments, setter methods or directly applying them to class properties. What this means in practice is that there is no longer a need for that level of abstraction that is required for service location to work. No registry is required (although one is usually implemented, I will discuss tools later).

By using dependency injection, our objects can be truly unaware of the particulars of any object it depends on. Not to be confused with the design pattern itself, Dependency Inversion Principle is one of the SOLID set of design principles that takes this a step further. It states that rather than depending on concrete implementations, we should depend on abstractions (read: interfaces, for modern PHP). An interface describes the method signature that any object implementing it should follow, a contract if you will.

By depending on this contract rather than the concrete implementation there are several benefits that become instantly apparent. We can mock an interface for testing before any concrete implementation is written knowing that it will follow the contract. We can also have multiple implementations of the same interface meaning our code is much more scalable and much less work is needed for swapping out implementations.

Tools

Initially I am only going to be talking about Service Locators and Dependency Injection Containers. In modern tools and libraries you will be hard pressed to find a difference between the two. This is due to the fact that, with modern tools, there isn't a difference in the tool itself but how it is used.

If you inject the tool in to your classes and request your dependencies from it, then you are using a Service Locator.

If your class has no knowledge of the container but the container handles the injection of your dependencies from another part of the system then you are using a Dependency Injection Container.

There are two reasons, in my opinion, why there is so much confusion around these tools. The simplest of the reasons is that they have different names and therefore people try to find a difference rather than just accepting the fact that they are named differently based on how they are intended to be used.

The second reason is that, in PHP, we are still in the infancy of the design patterns that these tools implement. This is why I used the term modern tools earlier in this section. As we have seen in previous examples, early implementations of service locators were not dependency injection containers. The easiest way I can think to summarise would be as follows.

Service Locators ARE NOT ALWAYS Dependency Injection Containers. Dependency Injection Containers ARE ALWAYS Service Locators.

Most modern frameworks are built on top of a container of some type so let's take a look at some examples of how they are used to implement either pattern.

Zend Framework 2

ZF2 is capable of using different strategies for resolving your objects. The main one being the ServiceManager component. By using this strategy, you are able to define your controllers as services and the ServiceManager will resolve those objects for you on routing to your controller.

This method is both Service Location and Dependency Injection.

Although simplified, this example shows best how a container can be used as both Service Locator and Dependency Injection Container in the same system. This also highlights why there is little or no difference between the tools as it is often used for both purposes.

As mentioned earlier, there are several other strategies that can be employed in ZF2 but this one best describes the solution to the problem from the perspective that we are viewing it.

Laravel 4

The genius behind the popularity of Laravel is it's ability to appeal to such a broad spectrum of developers. It has roots in Codeigniter and therefore some of it's user base also has roots in Codeigniter. Whilst Codeigniter was not necessarily good software design, nobody can deny it's ease of use. Laravel seems to have addressed this with a unique and more elegant solution to Service Location.

When you see a static call App::abort() in Laravel, you might be surprised to find out that it is not actually a static call to the logic itself. What is actually happening here is that you are statically accessing a facade that acts as a type service locator to return the object it is associated with.

This method provides a lower barrier of entry to the framework and in my opinion is one of the reasons it is so popular. With enough work put in to your test suites though, you could quite easily swap out facades and make them more testable whilst keeping your project accessible to a wide range of developers that might work on it.

At the other end of the spectrum and for the dependency injection purists out there (probably including myself), Laravel also has it's own Dependency Injection Container that resolves dependencies much in the same way as ZF2. The example below uses the Laravel IOC Container to do the same thing as ZF2 (It's the same image with a name change).

You will find that most frameworks employ a very similar dispatch process when it comes to their containers. The difference is purely implementation and how services and dependencies are stored or retrieved.

This subject would be a great standards project for the PHP-FIG and at that point would probably remove much of the confusion that comes with it.

Share on Google+