Wednesday, October 10, 2012

Distinct IoC, Service Locator & Dependency Injection Container (DIC)

The dependency injection pattern - A short example

<?php

class SomeClassWithDependencies
{
    public function __construct()
    {
        $this->dependency = new SomeObject();
        $this->config = Zend_Config_Ini(
            'path/to/my/configfile.ini', 
            APPLICATION_ENV
        );
    }
}
What we see here, is a class with several dependencies: The first two obvious dependencies are "SomeObject" and "Zend_Config_Ini", but addiotional with have also the dependency to the path of the config file ("path/to/my/configfile.ini") and the APPLICATION_ENV constant. So all together we have 4 dependencies, two of them to other objects. The problem starts, when I want to extend this class or I want to write unit tests and need to mock a lot of stuff. Let's improve this example and make it DI enabled.
<?php

class SomeClassWithDependencies
{
    public function __construct(
        SomeObject $someObject,
        Zend_Config $config
    ) {
        $this->dependency = $someObject;
        $this->config = $config;
    }
}
SomeClassWithDependencies is now DI enabled code. It has no hard dependencies on a concrete instance, instead it gets its dependencies injected. The main difference is now, how you use the class. Before the refactoring, you could simply instantiate the class and work with it. No you have to do some wiring upfront.
Before:
<?php

$object = new SomeClassWithDependencies();

After:
<?php

$someObject = new SomeObject();
$config = new Zend_Config_Ini('path/to/my/configfile.ini', APPLICATION_ENV);
$object = new SomeClassWithDependencies($someObject, $config);

If you use this class a couple of times in your application, you have redundant code everywhere you instantiate the class. Imagine even how complex things can be, if we have hundreds of classes like that. Where to put the wiring? The answer is: in a inversion of control container. An inversion of control container is simple object, that creates all services for you and holds the instantiation wiring.

Dependency Injection Container (DIC)

A DIC gets configured and is able to instantiate objects from its configuration. Ralph Schindler said: "DiC's require that you meta-program. You configure it, the container does all of the new calls. This means that instead of programming, you're metaprogramming." DIC's get their configuration and additional some DIC's (like Zend_Di) have the abbilities for auto-wiring and auto-instantiation.

Service Locator

A service locator instantiates objects by calling factories or closures. It isn't able to auto-wire or auto-instantiate. It can only create, what is defined in its factories and closures. However, when the service locator doesn't get injected into an object, so that the consuming object is able the pull soft dependencies from it, it isn't a real service locator, just a simple container that produces instances by name.
Other relavant posts:
Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler
Learning about dependency injection and PHP by Ralph Schindler