ramblings on PHP, SQL, the web, politics, ultimate frisbee and what else is on in my life
back 1  2  »  

Dependency injection container dilemma

I am currently working on adding the dependency injection container solution that Fabien created as part of the Symfony2 components effort to the okapi framework. Looking at what Fowler has to say he does not seem to be all that fond of such containers in general. But he seems to say of at all, you simply have a single service container class with static methods, which you then essentially have to hardcode or in the words of Fowler: "the decision between locator and injector depends on whether that dependency is a problem". So for Fowler things would look like this:


<?php

$foo = new foo(servicecontainer::getDB());

class foo {
    public function __construct($db) { 
        $this->db = $db; 
    }
}

?>

He thinks that the locator hides dependencies, but as Fabien explains, it gives you all sorts of was to visualize the dependencies and as such I can very well live with not seeing the dependencies in the constructor. Actually I could do a "best of both worlds", where I inject the container and the dependencies all as optional parameters. This way I have to pass the service container instance along, but I can actually make an instance of a different service container at any point if I want as well.


<?php
public function __construct($sc, $db = null) { 
    $this->db = is_null($db) ? $sc->db : $this->db = $db; 
}
?>

Of course I could also use a getter instead, to ensure I can still lazy load if its not guaranteed that I will need the db layer upon making an instance. Thats a definite plus for the following approach over the hardcoded static solution:


<?php
public function __construct($sc, $db = null) { 
    $this->sc = $sc; 
    $this->db = $db; 
}

public function getDB() {
    if (is_null($db)) {
         $this->db = $sc->db;
    }
    return $this->db; 

}

?>

Of course thats also kind of nasty in multiple ways. For one I need this kind of logic for every single parameter. Furthermore the fact that someone can just make an instance of a different service container and inject that can then lead to confusion, since you then loose the single point of reference. Finally it leads to recursive references, as you essentially need to store a reference to the service container in all instances, which are in turn references in the service container. This is never nice, though PHP is slowly becoming smarter in dealing with them in the various output functions and in PHP 5.3 there is even a way to clean up those circular references in long running scripts in case you have memory issues caused by this.

Anyways, it seems that at this point Fabien has also not really made up his mind. His PHP dumper class neither makes the methods static (to follow Fowlers approach), nor does it really default to injecting the service container instance. So anyone has already put his solution to the test?

Comments



Re: Dependency injection container dilemma

You should definitely check out Miško Hevery's blog (http://misko.hevery.com), he has quite a lot of very good and state-of-the-art posts about dependency injection (I actually consider the Martin Fowler paper a bit obsolete).

But for this post I can summarize some of his insightful advices:
- Do not pass around service locators. You might think that it's not that big a tradeoff, but it makes your classes harder to reuse (for at least other developers), because instead of declaring their dependencies explicitly in the constructor, they ask for a big haystack object, and you won't figure out easily what the bare minimum needed for that object to work is. That is a form of violating the Law of Démétér, and is a Bad Thing.
- You shouldn't worry about someone actually instantiating another instance of your service locator. Your so-called "single point of reference" should not be enforced by using a single object that contains a lot of unrelated objects. The only time you have to worry about multiple instances of a class that should only have one instance _in production_ is when you instantiate it. You have to create one instance in one place of the code, and pass it to every other constructor who declared it as a dependency.

And if you read enough from Misko, you should check sphicy at http://www.beberlei.de/sphicy/, it's practically a port of Google Guice, and as such a dependency injection framework, that supports constructor injection. It might as well suit your needs (or okapi's) better.

Re: Dependency injection container dilemma

Most of the time, you should NOT pass the service container object as an argument to your constructors. If you do that, you fall back to use the container as a service locator, which is not a good idea. Still, you can make SOME core classes "aware" of the service container (as it is called in Spring). Don't forget that the main goal of a dependency injection container is to be able to manage ANY class, without any knowledge of the container.

Also, the DI container has several other goals beside providing a good way to centralize your objects, like the configurability of your objects.

Re: Dependency injection container dilemma

5.3 addition of closures are going to make DI simpler I think, configuring the container is just going to register a interface with a closure to create an implementation of it.

Re: Dependency injection container dilemma

You wrote: "This way I have to pass the service container instance along, but I can actually make an instance of a different service container at any point if I want as well."

The PicoContainer site specifically mentions container dependence as an anti-pattern:

http://www.picocontainer.org/container-dependency-antipattern.html

It gives these reasons:

* It introduces an unneeded dependency from BImpl to the container, breaking the core principal of Inversion of Control

* This makes BImpl harder to unit test

* B assumes that the container has a registered an A. As a result, B won't fail fast if it has not. Instead, a will reference null, and BImpl will fail later.

Re: Dependency injection container dilemma

I think its slowly dawning on me. I need to move a bit of the logic out of the controller class into my front controller. Basically the controller class will only need to figure out what business logic to load (aka the command in okapi lingo, action in symfony lingo). It will then let the front controller make the instance of the given command. The command is of course also configured in the service container. So if the command needs a db connection, which in turn might need the log layer etc, all of this is configured in the service container. All is well ...

I think my brain just needs to overcome the fact that my "sheet of paper" is not empty, since I am refactoring an existing framework.

Re: Dependency injection container dilemma

I think it`s not an easy task to turn an existing framework into a DI based one. If you really want to get a good / complete integration it might be worth starting from scratch again, more or less. This is what I discovered myself, turning our framework into a DI based one more than three years ago.

Re: Dependency injection container dilemma

Regarding failing early, this is a standard issue with lazy loading. Obviously the more you lazy load, the later you know if whatever you are lazy loading is really available. Thats the name of the game. As such if one lazy load enables a class, one should give the option to not lazy load in those cases where you know for sure that you are going to need that external connection etc. either way.

Re: Dependency injection container dilemma

If want to lazy load to the extent outlined in the blog posting, then why does the class even have a $db property?

Surely the methods that require it, should have it passed into them?

function doSomething($db, .... )
{

$db->execute();
}

Re: Dependency injection container dilemma

Sure, thats another approach. I guess in this solution "optional" dependencies could be lazy loaded this way. But this brings us back to the issue of needed a reference to the servicecontainer or needed static methods to be able to get an instance anywhere.

In my current stage in refactoring I now have a fair bit of code in m frontcontroller that essentially, uses a "controller" to determine what route has matched and given that what command to load. Back in the front controller I set the returned route in the service container (since this is a dynamic parameter, I can simply define a "route" parameter in all relevant class configurations and then set the instance explicitly on the configured alias). Now I fetch an instance of the command from the service container. Inside the command, I can have all sorts of things going on.

A concret example is the soon to be released picok application. This app is similar to iGoogle. Users can load and organize any number of portlets. In a request on a given command method I therefore need to work with any number of these portlets at the same time. However some portlets might have different dependencies than the command. Worse yet, their might be portlets that share dependencies that are not matched by the command. Again to make things concret, we have various portlets (one for mail, one for todo's, one for calendar etc.) that fetch data from a Lotus Domini Notes server. How do I solve that?

Currently I am thinking I will simply define another service container that will be used on that command to manage the portlet dependencies. Now this all works nicely as long as none of the dependencies in the portlets cover a shared dependencies that is used in the rest of the system but not in the command. But here I do not yet have a concret example yet.

1  2  »