T
T
topuserman2019-09-10 21:27:08
PHP
topuserman, 2019-09-10 21:27:08

Dependency Injection Container, tried it in practice - did not understand the meaning?

Hello everyone, I started to study the dependency injection pattern, everything seems to be clear and logical.
As far as I understand, special containers are used for convenient dependency management.
For a very long time, I could not understand how this container is used in practice, what is the scope of the container, and at what point are services added to the container?
Having asked these questions on the forums, I was advised to take any DI container and try to work with it, then it will become clear.
I decided to take a very simple DI container, from the developers of symfony - Pimple .
Wrote the SystemBlock class (system block) with 3-4 dependencies:

  • Power Supply
  • video card
  • CPU
  • Operating system

An example of calling the SystemBlock class without a container:
<?php
use Less\SystemBlock;
 
$systemBlock = new SystemBlock\SystemBlock(
    new SystemBlock\GigabytePower,
    new SystemBlock\GeForceVideo,
    new SystemBlock\IntelProcessor
);
 
 
$os = new SystemBlock\LinuxOS;
$os->setUser(new SystemBlock\OSUser("Вася", "Vasya", true));
 
$systemBlock->setOperatingSystem($os);
$systemBlock->start();

and an example using a container:
<?php
use Less\SystemBlock;
 
$container = new \Pimple\Container;
 
$container["power"] = function ($c) {
    return new SystemBlock\GigabytePower;
};
 
$container["video"] = function ($c) {
    return new SystemBlock\GeForceVideo;
};
 
$container["processor"] = function ($c) {
    return new SystemBlock\IntelProcessor;
};
 
$container["system_block"] = function ($c) {
    return new SystemBlock\SystemBlock(
        $c["power"],
        $c["video"],
        $c["processor"]
    );
};
 
$systemBlock = $container["system_block"];
 
$os = new SystemBlock\LinuxOS;
$os->setUser(new SystemBlock\OSUser("Вася", "Vasya", true));
 
$systemBlock->setOperatingSystem($os);
$systemBlock->start();

I posted the files with classes and interfaces on GitHub
Questions that arose:
1. Did I understand correctly that we add services to the container in the file of a single entry point to the application?
What should I do if I have a case where I need to call the required configured service for uploading goods to the site, for example:
1.1. Loading occurs through the bin/import.php file when running the script through cron.
1.2. When loading occurs through the site's admin/import.php admin panel by clicking on the button.
In the first and second case, I need the same configured service. I need at the beginning of each file bin/import.php and admin/import.phpadd services to the container and configure, or do you need to put them in a separate common file? Please show an example of how this is done, and what can be the nuances?
. From my examples of managing dependencies through a container and manually, I didn't notice any difference. What is the advantage of a container? Why is what I did manually inconvenient?
or the container is needed in cases where the same service is used several times? And will it be convenient to test in the future? Replaced only in the container with a test service?
And to be honest, for me the most difficult thing is to understand the point
Where in the application does the container need to be initialized? Can it be put in a separate file? How to include this file? Etc.
I asked the same question on another forum, where I was even more confused, and now I don’t know if the forum member who wants to help me is right or not ..
I have been unable to overcome this topic for a long time ..

Answer the question

In order to leave comments, you need to log in

2 answer(s)
M
Maksim Fedorov, 2019-09-10
@topuserman

Container
The main task of a container is to reuse services more than once, for example, a certain provider in more than 1 place or a certain Sender in 20 places throughout the application. and make it easier to access them.
Moreover, it is the container that helps to simplify some other parts of the application, for example, the router, when we register routes and their handlers (controllers), we build the application in such a way that all the arguments of these handlers and the arguments of these arguments will be pulled from the container, that is, they will greatly reduce our problems with dependency management and make the router code itself (and other code) much simpler and more reliable.
Interaction of relatively simple objects / entities within the same layer is best done in your way - it is logical and understandable, and in general the container will only interfere, even more - out of place in this task. It was noted to you in the answer on that forum.
Modern containers actually provide sooo many more features: autowiring, when you don’t need to write a config, just write a class in the constructor, and if the container finds such a class, it will configure itself (and also take into account its constructor parameters), that is, the developer it remains to work only with unique and non-standard situations.
Or gives simplified factories/builders/decorators.
Container or not, with DI testing becomes possible by definition and will be relatively easy anyway.
The main thing is not to combine these two concepts. The pattern itself is just a special case of dependency inversion, the class received the dependency through the constructor / setter, and that’s it, it doesn’t know the details, therefore it’s easier to test - just replaced it with a dummy.
And the container is a tool for working with a complex application that injects just the same, that is, it performs the work of injecting. Central to it is the word container.
Regarding your entry points: multiple entry points = multiple initializations.
As if they are inherently different applications are already obtained by definition.
But you can simplify, and bring the initialization of the container into an abstraction - into a certain App / Kernel class and do it there, and at the entry point, initialize not the container over and over again, but the specific application.

P
postgresdev, 2019-09-10
@postgresdev

In general, I did not read the question.
When you need to test code: you need to replace objects in order not to test dependencies (during unit tests). That is, you will have a hard connection.
Plus, if you have experience, read 150 pages of perfect code, everything will fall into place.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question