DI does not necessarily work on the principle of lazy loading. Lazy loading is optimization already beyond what dependency injection is intended to do.
Dependency injection is first and foremost an abstract idea that does not have any uniquely correct implementation in the code. The essence of the idea is to refuse to manage dependencies by the objects themselves. That is, usually, in the refusal to create instances of any classes by the object itself, which are necessary for this object to work.
If your object itself creates some classes, and does not get them from the outside ready, then this is not dependency injection.
For example, you have a basket object that needs to save something in the database.
If you do not use DI, then the basket itself will need to get the DAO object from somewhere in order to save something in the database. Here you are immediately confronted with the need to use some kind of singleton, and with obvious problems when testing a basket object in isolation from some kind of database. The basket object will have to know what kind of DAO objects it works with.
In the case of using DI, the recycle bin object by default does not try to create an object, but expects (and provides methods) that it will be pointed out from outside, at or immediately after initialization, where and to which object should be accessed to add records to the database.
This approach offers many advantages, both by simplifying the classes themselves and by simplifying testing and changing. If you ever want to use a completely different DAO object, then for this you will not need to change the basket object, provided, of course, that the interface is waiting for an input, and not a specific object.
Of course, everything has its price. Programs that use DI become more difficult to understand, since the logic that used to be in one place turns out to be scattered throughout the program. Is it worth paying this price, and do you really need all the advantages of this approach - you need to decide carefully according to the actual situation. For some simple programs, you can do without DI at all. At the prototype stage, too, no DI may be needed. If you want to make understanding your program as difficult as possible, the use of DI is mandatory in all situations .
A related pattern for DI is the MVC pattern. The idea of MVC is somewhat more specific, because MVC implies specific roles for the constituent parts. You hide the database behind the controller, and the basket does not know anything about the database, but it knows which controller it should contact in case of any events. That is, you also come to the fact that you inject a dependency in the form of a controller into the basket object.
Practical example
You have an interface for getting and saving some data.
interface DAO { public function save($data); public function load(); }
You have a basket object that expects that during initialization it will be told where to go for data and where to save it.
class Cart { private $dao; private $items = []; public function __construct(DAO $dao) { $this->dao = $dao; $this->items = $this->dao->load(); } public function add($item, $qty = 1) { $this->items[$item] += $qty; $this->dao->save($this->items) } public function delete($item) { $this->items[$item] = 0; $this->dao->save($this->items) } /* Другие обычные методы пропустим */ }
You have a specific interface implementation.
class SessionDAO implements DAO { /* Методы инициализации придумайте сами */ public function save($data) { // куда-то сохраняете } public function load() { // откуда-то загружаете } }
Now you can create a DAO object yourself in the correct sequence, passing it to the basket object during initialization.
$dao = new SessionDAO(); $dao->initWithKey(/* ... */); $cart = new Cart($dao); $cart->add('Example', 10);
As you can see, the objects that the basket needs for work are not initialized anywhere in the basket. The basket receives all the necessary objects from the outside world.
There is no reason that would prevent you from making a lazy DAO that was initialized only at the time of use:
class LazySessionDAO implements DAO { private $dao; private function getConcreteDAO() { // отложенно инициализируем DAO при необходимости if (!$this->dao) { $this->dao = new SessionDAO(); $this->dao->initWithKey(/* ... */); } return $this->dao; } public function save($data) { $this->getConcreteDAO()->save($data); } public function load() { $this->getConcreteDAO()->load(); } }
As you can see, for this, absolutely nothing needed to be changed in the basket object. That is why dependency injection is so loved and appreciated.
Now to the Phemto mentioned in the article. This is dependency injection container or dependency injector , and not just dependency injection. One of many.