What’s the difference between a Dependency Injection Container and the Service Locator Pattern?

Dependency injection is important in modern coding practices. It allows us to help maintain our code by making it easier to test, and by making it less likely that dependent code will need to be changed if the dependency changes.

There are two different tools we use to make dependency injection easier, but they are easily confused and slightly different. So what is the difference between a Dependency Injection Container and the Service Locator pattern?

DICs and SLs have slightly different roles:

An implementation of the Service Locator pattern is an object that gets injected into classes that have dependencies.  From inside those classes, the Service Locator object can then be used to find and locate those dependencies when they’re needed to be used.

class Moo
{
    private $dependencyA;
    private $dependencyB;

    public function __construct($container)
    {
        $this->dependencyA = $container->get('DependencyA::class');
        $this->dependencyB = $container->get('DependencyB::class');
    }
}

Dependency Injection Containers are quite similar, although they have a subtle but important difference.  Dependency Injection Containers don’t get injected into classes themselves, they locate the required objects and inject those objects and instead inject those into the class.

class MooFactory
{
    public function create($container)
    {
        $dependencyA = $container->get('DependencyA::class');
        $dependencyB = $container->get('DependencyB::class');

        return new Moo($dependencyA, $dependencyB);
    }
}

class Moo
{
    private $dependencyA;
    private $dependencyB;

    public function __construct($dependencyA, $dependencyB)
    {
        $this->dependencyA = $dependencyA;
        $this->dependencyB = $dependencyB;
    }
}

In the example above we use a factory to fetch the dependencies and inject them into our class, which creates a looser coupling between your code and the container. If the container ever needs to be switched out, classes themselves never have to be changed, only the factories in this example. The responsibility of fetching dependencies are separated from the class’s responsibility so it’s a lot easier to test for these specific changes when you come to change your container.

There is another maintenance advantage to using a Dependency Injection Container over a Service Locator. If you have a look at the constructors of both examples, we can quickly see what our class requires to work in the second example. In the first example, the only dependency we can see in the constructor is the container itself, which hides the true dependencies somewhere else in the class’s code.

Interestingly, Zend Framework’s Service Manager has recently gone from being described on the Zend Framework website as an implementation of a Service Locator to being called a Dependency Injection Container. This is probably to discourage people from directly injecting the container into controllers, and instead using a factory to injection actual dependencies into controllers.