What is Dependency Injection?

I’ve answered this a couple times so I’m writing it down 🙂

DI is a way to separate services from the code that uses those services.

DI makes testing easier. It also allows the services to be replaced with alternatives – but this is less important than the testing.

DI is used in Angular, Zend Framework 2, Symfony and more frameworks.

DI is implemented as an array or dictionary of services with generic names. Services are created during startup, and registered.

For example, suppose I created a service to do HTTP requests, JohnsHttpRequestService, and registered as ‘http’. There’s a concrete service, but it’s registered with a generic name.

Later, during testing, the service can be replaced. For example, ‘http’ could be re-registered with RosasHttpTestingService, which doesn’t make real HTTP requests, but fakes them, and does it 100x faster.

In most frameworks, the service directory is implemented as a dictionary or array of objects.

The “injection” is performed by the framework. In Angular, it looks for specific parameter names in the controller function declaration:

controller('FooCtrl', function ($http) { ... });

It sees “$http” and looks for the “$http” service, and then passes it in as an argument. (To do this, it’s setting up an environment to execute the controller, with $http defined as a specific service, then executes the function.)

Each of these injectable services has a “manager” defined. The service is a thin shell over the manager, and when you are doing testing, you swap out the manager.

In Symfony, you have a thing called a $container that is an object that holds all the services.

$HttpManager = $container->get('http');

It’s pretty involved to set up the service, so look at the Symfony docs to see how it’s done.

In Zend Framework 2, the syntax is similar:

$HttpService = $container->get('http');

The ZF2 tutorial, however, doesn’t explain this well. Their idea of DI is to inject by specifying the class name, rather than a generic name. Then, to make generic names, you create ServiceLocators.

(I first learned DI via ZF2. Consequently, I was confused about what DI was for.)

Performance in PHP

In PHP, each page load brings in the entire framework, and rebuilds it, so setting up the DI container with all the services would be, potentially, very slow.

So, what you do is set up the container without the services. You create the container with configuration information that can instantiate the service. This configuration info can be an array or string or something lightweight and constant. Then, when ->get(‘servicename’) is called, it searches the container, finds the class to instantiate, creates the service object (which is usually a singleton), and then returns that.

This loading slowdown isn’t an issue in a one-page Javascript app, so Angular doesn’t bother with configs. You just create the service and it’s registered automatically. The injection happens with the controllers are created.

Likewise, a slowdown’s not an issue in application servers that don’t do a clean reload for each request.

Layers of indirection

DI introduces a layer of indirection to services. You now refer to services by a generic name, rather than a specific class name.

That means each service must implement the same interface.

Implementing a common interface, is, itself, like a layer of indirection. It allows you to use different classes in the same way.

This contrasts starkly with using a specific library, with its specific library calls. You can’t easily swap out libraries (unless they implement the same interfaces).

So, in a framework with DI, you have two layers of indirection, giving you flexibility.