My understanding of the essence of depency injection. Tezisno.

All classes are called through the DI class method. The DI class attribute contains a list of class dependencies. Inside DI, classes whose dependencies are described, are invoked the same way - via DI, implementing them all.

DI works on the principle of lazy loading. That is, it creates objects only when necessary, the rest of the time only keeps the correspondences of the interfaces to the implementations and their internal settings.

An example of the work of DI .

  • I am compiling a list (array) of the correspondence between classes and their dependencies.
  • When I try to create an object through DI (conditionally $DI->create('\namespace\ClassName') instead of new \namespace\ClassName ), I check if there are dependencies in the array for the called class and, if necessary, I substitute certain arguments with the necessary classes in accordance with rules from the array. After that, I return the created object.

Did I understand the algorithm of depency injection?

If there are constructive comments, correct it (preferably with links).

ps understanding of the principle of work made after reading this

    1 answer 1

    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.

    • I correctly understand that the "inversion" in DI is achieved precisely because the object is created not directly, but through an intermediary who knows how to create it correctly? - DeBill
    • That you are using a different design pattern describes. In the case of pure DI, users generally do not create objects that they use. They are being introduced to them from the outside . This achieves freedom from specific dependencies and all other advantages of DI. - sanmai
    • With your approach, you cannot just change any dependencies, for example, for testing, because your object itself controls its dependencies, creates them, etc. This is not DI. - sanmai
    • In the article about which I said the factory method is also described? At least scroll through the page. It describes the mechanism that remembers the interface compliance with implementations and when creating objects through it, inserts the required interface into the constructor arguments — classes that implement this interface. He collects these classes from the global register or, if the user has specified specific matches, from the user (local). If it is not a DI, describe how it works. That is what I am looking for on the net. Everything is described too mechanically - DeBill
    • You asked what is DI? Please, ready. - sanmai