Advantages for using Dependency Injection Container?

Well this is not a thread for debating the usage of DI, I fully acknowledge that DI is the ideal solution for my framework and application and I am talking about a sub-concept, the Dependency Injection Container(DIC). In my framework and application I have been always injecting dependency directly at each class’ constructor, and in most cases the classes will not need more than three dependencies(otherwise I will think about refactoring as the class responsibility may be messed up). I heard that Dependency Injection is good when you want deeply nested dependency object, like 5-6 level deep. I will agree on this, but isnt it a poor application design if a class needs such as deep dependency in the first place? Or am I missing something here? What are the other benefits of DIC? How does having a specific container help with dependency injection, as compared to the standard constructor injection technique? Can anyone illustrate with an example? Thanks.

The big benefit that I see from DI is uniit testing.

This is demonstrable with a two line before/after:

Without a container, you might need something like

$a = new A(new B, new C, new D(new E, new F, new G), new H(new I(new J)));

with a container, this becomes:

$a = $container->create('A');

Containers can also do some useful things, such as anything that needs a PDO instance can be given the same instance. So if A and B need need PDO:


$a = $container->create('A');
$a = $container->create('B');

Will automatically be given the same instance of PDO if they both require it. This enables you to apply a consistent set of rules across your project while allowing another project to use the same classes but a different set of rules.

edit: Shamless plug: I have a long list of examples of what kind of things contianer can do on Dice, my own container’s website: https://r.je/dice.html

I will agree on this, but isnt it a poor application design if a class needs such as deep dependency in the first place?

It really depends on how you’re defining “poor”. A higher number of smaller classes makes the application a lot more flexible and easy to modify at a cost of more classes. Smaller classes with fewer methods are generally considered a good thing, however.

I see, its very informative. I did read your article, it was very well-written, though I was still a bit confused why we use DIC when you can just inject dependency individually. I guess, the DIC is not a service locator, but a factory that abstract away the possibly complex object creation process, am I right?

I think it’s easiest to answer this if we break apart a DI container’s core responsibilities: that of a factory and a registry of singletons.

The factory aspect might not be seen as an advantage per se, but it compensates for what would otherwise be a drawback of DI. That drawback is that creation of an object becomes more complicated.

// If we're using service locator, then creation is simple
$x = new X();

// But if we're using DI, then creation can become complicated
$y = new Y();
$z = new Z();
$x = new X($y, $z);

We’d like to encapsulate that creation complexity in a factory method.

class DiFactory
{
    public function getX()
    {
        $y = new Y();
        $z = new Z();
        $x = new X($y, $z);

        return $x;
    }
}

Now creation is simple again.

$x = $diFactory->getX();

But the registry of singletons aspect is, I think, a clear advantage. It solves the problem of globals. There’s always going to be objects or values that need to be shared throughout your application. A logger or cache object are easy examples. So where do we store and how do we access those shared instances? The original solution was global variables. Then we tried static class properties. Then we tried having a class impose its own singleton-ness. Today, we create an object whose job is to hold (or contain, hence “container”) those shared instances.

class GlobalsContainer
{
    private $singletons = [];

    // ...

    public function getX()
    {
        if (!isset($this->singletons['x'])) {
            $this->singletons['x'] = $this->diFactory->getX();
        }

        return $this->singletons['x'];
    }
}

A DI container is generally a combination of those two responsibilities, factory and registry of singletons.

class DiContainer
{
    private $singletons = [];

    // ...

    public function getX()
    {
        if (!isset($this->singletons['x'])) {
            $y = new Y();
            $z = new Z();
            $x = new X($y, $z);

            $this->singletons['x'] = $x;
        }

        return $this->singletons['x'];
    }
}

The above describes the bare bones responsibilities, but specific containers can offer a variety of other features. Some allow you to define and configure services through a config file (XML, Yaml, or whatever). Some will use reflection to try to deduce the creation logic automagically. And some offer scopes – a singleton-ish service that’s tied to the lifetime of another object or context. A common example is an application that can handle a master HTTP request as well as any number of sub-requests. If you ask for the request service, then you get the same instance each time, but only within the scope the current request.

Hmm. Possibly, but not necessarily. I don’t think I’m ready to call that a code smell. But if you’re unsure, feel free to post more details about what depends on what, and we can see if anything jumps out at us.

1 Like

To extend on what I was saying before, one advantage is productivity. Depending on the DIC, you can generally develop a project faster.

With an auto-configuring DIC that relies on type-hinting, say I have already have a class SomeClass being created by the DIC. Its constructor is currently arugmentless:

class SomeClass {
    public function __construct() {}
}

If I want it it to use my PDO instance, I make a change to the class:

class SomeClass {
    public function __construct(PDO $pdo) {
         $this->pdo = $pdo;
    }
}

and it just works without making any other changes to any other files. SomeClass now has access to the PDO instance managed by the DIC without any other changes anywhere in the application :slight_smile: This means I can add/remove dependencies to constructor arguments on a whim, nowhere else ever needs updating unless I want to do something special such as a new PDO instance.

This makes for incredibly loose coupling as no class’ constructor is ever directly referenced anywhere else in the code, so its arguments can change without ever breaking any other code.

I see, it was very descriptive and good to read, thank you Jeff Mott. I absolutely agree on the factory aspect, it will make projects a lot simpler and readable. I dont know if I will agree with the singleton aspect though, as singleton itself is an anti-pattern, isnt it?

Thank you again for this follow-up comment, TomB, it was very informative. I like how it promotes loose-coupling.

1 Like

When a class imposes its own singleton-ness, that’s considered bad. Instead, we use the container to restrict instantiation to just one object.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.