2

does dependency injection make it impossible to lazy load dependencies?

because of the fact that you need to pass instances to the constructor, you cannot make the instances later

xpedobearx
  • 557
  • 2
  • 6
  • 12
  • Are you wanting to lazy load because something is not always needed? I.e. it's not a dependency? – Jonnix Oct 30 '16 at 11:39
  • its a dependecy but its used in another method that may be called or not – xpedobearx Oct 30 '16 at 11:42
  • Then you probably want setters rather than passing it into a constructor if there are times it's not required. Then you can load when necessary. – Jonnix Oct 30 '16 at 11:43
  • I know that Symfony's DI container allows that, when the dependecy ocramius/proxy-manager is installed. – SenseException Oct 30 '16 at 11:45

2 Answers2

3

does dependency injection make it impossible to lazy load dependencies?

No, it does not make lazy loading impossible. I would even argue the opposite and I would even say that dynamic languages (like PHP and Ruby) make this even easier than statically typed languages (like Java and C#).

Since your components program to abstractions, it is easy to inject an implementation for that same contract that wraps the real implementation that can be lazy loaded. The wrapper acts like a proxy and this allows the consumer to stay oblivious to the fact that any lazy loading is even going on. This increases maintainability.

My PHP is quite rusty, so the next example might not be 'compiling' (any PHP dev, please feel free to update this example), but the following show the idea.

Here we see some class that implements some business logic (ShipOrderHandler) and it depends on a logger dependency:

class ShipOrderHandler {
    private $logger;

    function __construct($logger) {
        $this->logger = $logger;
    }

    function Handle($command) {
        $this->logger->Log("shipping order " + $command->OrderId);
    }
}

class DatabaseLogger {

    function Log($message) {
        // log the message
    }
}

This is how we might construct an object graph for the ShipOrderHandler:

$handler = new ShipOrderHandler(new DatabaseLogger());

No imagine our DatabaseLogger to be actually heavy to initialize. Using the power of DI and dynamic languages, we can simply add a proxy implementation for our logger abstract; let's call it LazyLogger. Our new LazyLogger might look as follows:

class LazyLogger {
    private $loggerFactory;
    private $logger;
    function __construct($loggerFactory) {
        $this->loggerFactory = $loggerFactory;
    }

    function Log($message) {
        if (!$this->logger) $logger = $loggerFactory();
        $this->logger.Log($message);
    }
}

We can now apply this LazyLogger to our Composition Root to make DatabaseLogger created lazily, without making any change to our application components:

$handler = new ShipOrderHandler(
    new LazyLogger(function() { return new DatabaseLogger() });

Here we wrap the creation of DatabaseLogger in an anonymous function that is injected into the LazyLogger. When the LazyLogger's Log method is called for the first time, the factory method is called, DatabaseLogger is created and the LazyLogger will cache that instance and will use it until it goes out of scope itself.

As you can see, by no means does Dependency Injection actually limit the use of lazy loading; on the contrary: it allows lazy loading without the need to make sweeping changes throughout the application.

This is the power of DI.

Steven
  • 151,500
  • 20
  • 287
  • 393
0

You'll find a Symfony DI component example in http://php.budgegeria.de/flzsbalQV2 where lazy loading is used.

The class Bar has a dependency to Foo, that is configured as a service. When Foo is instanciated, an echo will run. As you can see, the instance will be created when the first access to a Foo dependency method happens.

You need the ocramius/proxy-manager composer package for that. It is also possible to implement this lazy behaviour to other DI containers.

SenseException
  • 885
  • 10
  • 14