From 730091ad58f7dcacc65ba22e06b217fe73f9f9f4 Mon Sep 17 00:00:00 2001 From: Arduanov Danil Date: Sat, 3 Feb 2024 19:30:07 +0500 Subject: [PATCH 1/4] Start translate --- guide/ru/concept/di-container.md | 247 +++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 guide/ru/concept/di-container.md diff --git a/guide/ru/concept/di-container.md b/guide/ru/concept/di-container.md new file mode 100644 index 0000000..c0776ee --- /dev/null +++ b/guide/ru/concept/di-container.md @@ -0,0 +1,247 @@ +# Dependency injection and container +# Внедрение зависимостей и контейнер внедрения зависимостей + +## Dependency injection +## Внедрение зависимостей + +There are two ways of re-using things in OOP: inheritance and composition. +В ООП существует два способа повторного использования кода: наследование и композиция. + +Inheritance is simple: +Наследование - это просто: + +```php +class Cache +{ + public function getCachedValue($key) + { + // .. + } +} + +class CachedWidget extends Cache +{ + public function render(): string + { + $output = $this->getCachedValue('cachedWidget'); + if ($output !== null) { + return $output; + } + // ... + } +} +``` + +The issue here is that these two are becoming unnecessarily coupled or inter-dependent making them more fragile. +Проблема здесь в том, что эти два класса становятся излишне связанными или взаимозависимыми, что делает их более хрупкими. + +Another way to handle this is composition: +Есть способ справиться с этой проблемой — композиция: + +```php +interface CacheInterface +{ + public function getCachedValue($key); +} + +final class Cache implements CacheInterface +{ + public function getCachedValue($key) + { + // .. + } +} + +final class CachedWidget +{ + private CacheInterface $cache; + + public function __construct(CacheInterface $cache) + { + $this->cache = $cache; + } + + public function render(): string + { + $output = $this->cache->getCachedValue('cachedWidget'); + if ($output !== null) { + return $output; + } + // ... + } +} +``` + +We've avoided unnecessary inheritance and used interface to reduce coupling. You can replace cache +implementation without changing `CachedWidget` so it's becoming more stable. +Мы избежали ненужного наследования и использовали интерфейс, чтобы уменьшить связанность. Вы можете заменить реализацию кэша без изменения класса `CachedWidget`, поэтому он становится более стабильным. + +The `CacheInterface` here is a dependency: an object another object depends on. +The process of putting an instance of dependency into an object (`CachedWidget`) is called dependency injection. +Здесь `CacheInterface` это зависимость - объект, от которого зависит другой объект. Процесс помещения экземпляра объекта зависимости в объект (`CachedWidget`) называется внедрением зависимости. + +There are many ways to perform it: +Существует множество способов его реализации: + +- Constructor injection. Best for mandatory dependencies. +- Внедрение через конструктор. Лучше всего подходит для обязательных зависиомостей. +- Method injection. Best for optional dependencies. +- Через метод. Лучше использовать для необязательных зависимостей. +- Property injection. Better to be avoided in PHP except maybe data transfer objects. +- Через свойство. Лучше избегать использования в PHP, за исключением, может быть, объектов передачи данных (DTO) + + +## DI container +## Контейнер внедрения зависимостей + +Injecting basic dependencies is simple and easy. You're choosing a place where you don't care about dependencies, +which is usually an action handler, which you aren't going to unit-test ever, create instances of dependencies needed +and pass these to dependent classes. +Внедрять базовые зависимости просто и легко. Вы выбираете место где вас не волнуют зависимости, которые обычно являются обработчиками действий и которые вы не собираетесь тестировать, создаете экземпляры необходимых зависимостей и передаете их в зависимые классы. + +It works well when there aren't many dependencies overall and when there are no nested dependencies. When there are +many and each dependency has dependencies itself, instantiating the whole hierarchy becomes a tedious process, which +requires lots of code and may lead to hard to debug mistakes. +Это хорошо работает, когда в целом зависимостей немного и нет вложенных зависимостей. Когда их много, и каждая зависимость сама имеет зависимости, создание всей иерархии становится утомительным процессом, который требует большого количества кода и может привести к трудно отлаживаемым ошибкам. + +Additionally, lots of dependencies, such as certain third party API wrapper, are the same for any class using it. +Кроме того, многие зависимости, такие как некоторые сторонние обертки API, одинаковы для любого класса, использующего его. +So it makes sense to: +Поэтому имеет смысл: + +- Define how to instantiate such API wrapper once. +- Определить, как создать экземпляр такой обертки API один раз. +- Instantiate it when required and only once per request. +- Создавать его экземпляр при необходимости и только один раз за запрос. + +That's what dependency containers are for. +Именно для этого нужны контейнеры зависимостей. + +A dependency injection (DI) container is an object that knows how to instantiate and configure objects and +all their dependent objects. [Martin Fowler's article](https://martinfowler.com/articles/injection.html) has well +explained why DI container is useful. Here we will mainly explain the usage of the DI container provided by Yii. +Контейнер внедрения зависимостей (DI-контейнер) - это объект, который знает, как создавать и настраивать объекты и все зависимые от них объекты. [Статья Мартина Фаулера](https://martinfowler.com/articles/injection.html) хорошо объясняет почему DI-контейнер полезен. Здесь мы в основном поясним использование DI-контейнера, предоставляемого Yii. + +Yii provides the DI container feature through the [yiisoft/di](https://github.com/yiisoft/di) package and +[yiisoft/injector](https://github.com/yiisoft/injector) package. +Yii реализует DI-контейнер через пакет [yiisoft/di](https://github.com/yiisoft/di) и [yiisoft/injector](https://github.com/yiisoft/injector). + +### Configuring container +### Конфигурирование контейнера + +Because to create a new object you need its dependencies, you should register them as early as possible. +Поскольку для создания нового объекта вам нужны его зависимости, вам следует зарегестрировать их как можно раньше +You can do it in the application configuration, `config/web.php`. For the following service: +Вы можете сделать это в конфигурации приложения, `config/web.php`. Для следующего сервиса: + +```php +class MyService implements MyServiceInterface +{ + public function __construct(int $amount) + { + } + + public function setDiscount(int $discount): void + { + + } +} +``` + +configuration could be: +конфигурация может быть: + +```php +return [ + MyServiceInterface::class => [ + 'class' => MyService::class, + '__construct()' => [42], + 'setDiscount()' => [10], + ], +]; +``` + +That's equal to the following: +Это соответствует: + +```php +$myService = new MyService(42); +$myService->setDiscount(10); +``` + +There are extra methods of declaring dependencies: +Существуют дополнительные методы объявления зависимостей: + +```php +return [ + // declare a class for an interface, resolve dependencies automatically + // объявить класс для интерфейса, автоматически разрешить зависимости + EngineInterface::class => EngineMarkOne::class, + + // array definition (same as above) + // определение в массиве (то же, что и выше) + 'full_definition' => [ + 'class' => EngineMarkOne::class, + '__construct()' => [42], + '$propertyName' => 'value', + 'setX()' => [42], + ], + + // closure + // замыкание + 'closure' => static function(ContainerInterface $container) { + return new MyClass($container->get('db')); + }, + + // static call + // статический вызов + 'static_call' => [MyFactory::class, 'create'], + + // instance of an object + // экземпляр объекта + 'object' => new MyClass(), +]; +``` + +### Injecting dependencies + +Directly referencing container in a class is a bad idea since the code becomes non-generic, coupled to container interface +and, what's worse, dependencies are becoming hidden. Because of that, Yii inverts the control by automatically injecting +objects from a container in some constructors and methods based on method argument types. + +This is primarily done in constructor and handing method of action handlers: + +```php +use \Yiisoft\Cache\CacheInterface; + +class MyController +{ + private CacheInterface $cache; + + public function __construct(CacheInterface $cache) { + $this->cache = $cache; + } + + public function actionDashboard(RevenueReport $report) + { + $reportData = $this->cache->getOrSet('revenue_report', function() use ($report) { + return $report->getData(); + }); + + return $this->render('dashboard', [ + 'reportData' => $reportData, + ]); + } +} +``` + +Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that instantiates and calls action handler, it +checks the constructor and method argument types, get dependencies of these types from a container and pass them as +arguments. That's usually called auto-wiring. It happens for sub-dependencies as well, that's if you don't give dependency +explicitly, container would check if it has such a dependency first. +It's enough to declare a dependency you need, and it would be got from a container automatically. + + +## References + +- [Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler](https://martinfowler.com/articles/injection.html) From 4aa735ed41d4dd793f6436af45510b08f2a17f8e Mon Sep 17 00:00:00 2001 From: Arduanov Danil Date: Sun, 4 Feb 2024 09:15:39 +0500 Subject: [PATCH 2/4] Finish translate --- guide/ru/concept/di-container.md | 89 ++++++++++---------------------- 1 file changed, 26 insertions(+), 63 deletions(-) diff --git a/guide/ru/concept/di-container.md b/guide/ru/concept/di-container.md index c0776ee..4de1517 100644 --- a/guide/ru/concept/di-container.md +++ b/guide/ru/concept/di-container.md @@ -1,13 +1,9 @@ -# Dependency injection and container # Внедрение зависимостей и контейнер внедрения зависимостей -## Dependency injection ## Внедрение зависимостей -There are two ways of re-using things in OOP: inheritance and composition. В ООП существует два способа повторного использования кода: наследование и композиция. -Inheritance is simple: Наследование - это просто: ```php @@ -32,10 +28,8 @@ class CachedWidget extends Cache } ``` -The issue here is that these two are becoming unnecessarily coupled or inter-dependent making them more fragile. -Проблема здесь в том, что эти два класса становятся излишне связанными или взаимозависимыми, что делает их более хрупкими. +Проблема здесь в том, что эти два класса становятся излишне сопряженными или взаимозависимыми, что делает их более хрупкими. -Another way to handle this is composition: Есть способ справиться с этой проблемой — композиция: ```php @@ -72,67 +66,45 @@ final class CachedWidget } ``` -We've avoided unnecessary inheritance and used interface to reduce coupling. You can replace cache -implementation without changing `CachedWidget` so it's becoming more stable. -Мы избежали ненужного наследования и использовали интерфейс, чтобы уменьшить связанность. Вы можете заменить реализацию кэша без изменения класса `CachedWidget`, поэтому он становится более стабильным. +Мы избежали ненужного наследования и использовали интерфейс, чтобы уменьшить сопряженность. +Вы можете заменить реализацию кэша без изменения класса `CachedWidget`, поэтому он становится более стабильным. -The `CacheInterface` here is a dependency: an object another object depends on. -The process of putting an instance of dependency into an object (`CachedWidget`) is called dependency injection. -Здесь `CacheInterface` это зависимость - объект, от которого зависит другой объект. Процесс помещения экземпляра объекта зависимости в объект (`CachedWidget`) называется внедрением зависимости. +Здесь `CacheInterface` это зависимость - объект, от которого зависит другой объект. +Процесс помещения экземпляра объекта зависимости в объект (`CachedWidget`) называется внедрением зависимости. -There are many ways to perform it: Существует множество способов его реализации: -- Constructor injection. Best for mandatory dependencies. - Внедрение через конструктор. Лучше всего подходит для обязательных зависиомостей. -- Method injection. Best for optional dependencies. - Через метод. Лучше использовать для необязательных зависимостей. -- Property injection. Better to be avoided in PHP except maybe data transfer objects. - Через свойство. Лучше избегать использования в PHP, за исключением, может быть, объектов передачи данных (DTO) - -## DI container ## Контейнер внедрения зависимостей -Injecting basic dependencies is simple and easy. You're choosing a place where you don't care about dependencies, -which is usually an action handler, which you aren't going to unit-test ever, create instances of dependencies needed -and pass these to dependent classes. -Внедрять базовые зависимости просто и легко. Вы выбираете место где вас не волнуют зависимости, которые обычно являются обработчиками действий и которые вы не собираетесь тестировать, создаете экземпляры необходимых зависимостей и передаете их в зависимые классы. +Внедрять базовые зависимости просто и легко. +Вы выбираете место где вас не волнуют зависимости, которые обычно являются обработчиками действий и которые вы не собираетесь тестировать, создаете экземпляры необходимых зависимостей и передаете их в зависимые классы. -It works well when there aren't many dependencies overall and when there are no nested dependencies. When there are -many and each dependency has dependencies itself, instantiating the whole hierarchy becomes a tedious process, which -requires lots of code and may lead to hard to debug mistakes. -Это хорошо работает, когда в целом зависимостей немного и нет вложенных зависимостей. Когда их много, и каждая зависимость сама имеет зависимости, создание всей иерархии становится утомительным процессом, который требует большого количества кода и может привести к трудно отлаживаемым ошибкам. +Это хорошо работает, когда в целом зависимостей немного и нет вложенных зависимостей. +Когда их много, и каждая зависимость сама имеет зависимости, создание всей иерархии становится утомительным процессом, который требует большого количества кода и может привести к трудно отлаживаемым ошибкам. -Additionally, lots of dependencies, such as certain third party API wrapper, are the same for any class using it. Кроме того, многие зависимости, такие как некоторые сторонние обертки API, одинаковы для любого класса, использующего его. -So it makes sense to: Поэтому имеет смысл: -- Define how to instantiate such API wrapper once. - Определить, как создать экземпляр такой обертки API один раз. -- Instantiate it when required and only once per request. - Создавать его экземпляр при необходимости и только один раз за запрос. -That's what dependency containers are for. Именно для этого нужны контейнеры зависимостей. -A dependency injection (DI) container is an object that knows how to instantiate and configure objects and -all their dependent objects. [Martin Fowler's article](https://martinfowler.com/articles/injection.html) has well -explained why DI container is useful. Here we will mainly explain the usage of the DI container provided by Yii. -Контейнер внедрения зависимостей (DI-контейнер) - это объект, который знает, как создавать и настраивать объекты и все зависимые от них объекты. [Статья Мартина Фаулера](https://martinfowler.com/articles/injection.html) хорошо объясняет почему DI-контейнер полезен. Здесь мы в основном поясним использование DI-контейнера, предоставляемого Yii. +Контейнер внедрения зависимостей (DI-контейнер) - это объект, который знает, как создавать и настраивать объекты и все зависимые от них объекты. +[Статья Мартина Фаулера](https://martinfowler.com/articles/injection.html) хорошо объясняет почему DI-контейнер полезен. +Здесь мы в основном поясним использование DI-контейнера, предоставляемого Yii. -Yii provides the DI container feature through the [yiisoft/di](https://github.com/yiisoft/di) package and -[yiisoft/injector](https://github.com/yiisoft/injector) package. Yii реализует DI-контейнер через пакет [yiisoft/di](https://github.com/yiisoft/di) и [yiisoft/injector](https://github.com/yiisoft/injector). -### Configuring container ### Конфигурирование контейнера -Because to create a new object you need its dependencies, you should register them as early as possible. -Поскольку для создания нового объекта вам нужны его зависимости, вам следует зарегестрировать их как можно раньше -You can do it in the application configuration, `config/web.php`. For the following service: -Вы можете сделать это в конфигурации приложения, `config/web.php`. Для следующего сервиса: +Поскольку для создания нового объекта вам нужны его зависимости, вам следует зарегестрировать их как можно раньше. +Вы можете сделать это в конфигурации приложения, `config/web.php`. +Например, для следующего сервиса: ```php class MyService implements MyServiceInterface @@ -148,7 +120,6 @@ class MyService implements MyServiceInterface } ``` -configuration could be: конфигурация может быть: ```php @@ -161,7 +132,6 @@ return [ ]; ``` -That's equal to the following: Это соответствует: ```php @@ -169,16 +139,13 @@ $myService = new MyService(42); $myService->setDiscount(10); ``` -There are extra methods of declaring dependencies: Существуют дополнительные методы объявления зависимостей: ```php return [ - // declare a class for an interface, resolve dependencies automatically // объявить класс для интерфейса, автоматически разрешить зависимости EngineInterface::class => EngineMarkOne::class, - // array definition (same as above) // определение в массиве (то же, что и выше) 'full_definition' => [ 'class' => EngineMarkOne::class, @@ -187,29 +154,26 @@ return [ 'setX()' => [42], ], - // closure // замыкание 'closure' => static function(ContainerInterface $container) { return new MyClass($container->get('db')); }, - // static call // статический вызов 'static_call' => [MyFactory::class, 'create'], - // instance of an object // экземпляр объекта 'object' => new MyClass(), ]; ``` -### Injecting dependencies +### Внедрение зависимостей + +Непосредственное обращение к контейнеру в классе - плохая идея, так как код становится неуниверсальным, сопряжен с интерфейсом контейнера и, что еще хуже, зависимости становятся скрытыми. -Directly referencing container in a class is a bad idea since the code becomes non-generic, coupled to container interface -and, what's worse, dependencies are becoming hidden. Because of that, Yii inverts the control by automatically injecting -objects from a container in some constructors and methods based on method argument types. +Поэтому Yii инвертирует управление, автоматически вводя объекты из контейнера в конструкторы и методы, основываясь на типах аргументов. -This is primarily done in constructor and handing method of action handlers: +В основном это делается в конструкторе и методе, обрабатывающем действие: ```php use \Yiisoft\Cache\CacheInterface; @@ -235,13 +199,12 @@ class MyController } ``` -Since it's [yiisoft/injector](https://github.com/yiisoft/injector) that instantiates and calls action handler, it -checks the constructor and method argument types, get dependencies of these types from a container and pass them as -arguments. That's usually called auto-wiring. It happens for sub-dependencies as well, that's if you don't give dependency -explicitly, container would check if it has such a dependency first. -It's enough to declare a dependency you need, and it would be got from a container automatically. +Поскольку именно [yiisoft/injector](https://github.com/yiisoft/injector) создает экземпляр и вызывает обработчик действий - он проверяет типы аргументов конструктора и метода, получает зависимости этих типов из контейнера и передает их как аргументы. +Обычно это называется автоматическим разрешением зависимостей. +Это происходит и с дополнительными зависимостями - если вы явно не указываете зависимость, контейнер сначала проверит, есть ли у него такая зависимость. +Достаточно объявить нужную вам зависимость, и она будет получена из контейнера автоматически. -## References +## Полезные ссылки -- [Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler](https://martinfowler.com/articles/injection.html) +- [Inversion of Control Containers and the Dependency Injection pattern Мартин Фаулер](https://martinfowler.com/articles/injection.html) From 962b75fc6c3d466fbf11e34716c4397f4fe6b32b Mon Sep 17 00:00:00 2001 From: Arduanov Danil Date: Sun, 4 Feb 2024 09:30:14 +0500 Subject: [PATCH 3/4] Delete extra empty stirng --- guide/ru/concept/di-container.md | 1 - 1 file changed, 1 deletion(-) diff --git a/guide/ru/concept/di-container.md b/guide/ru/concept/di-container.md index 4de1517..12688e6 100644 --- a/guide/ru/concept/di-container.md +++ b/guide/ru/concept/di-container.md @@ -204,7 +204,6 @@ class MyController Это происходит и с дополнительными зависимостями - если вы явно не указываете зависимость, контейнер сначала проверит, есть ли у него такая зависимость. Достаточно объявить нужную вам зависимость, и она будет получена из контейнера автоматически. - ## Полезные ссылки - [Inversion of Control Containers and the Dependency Injection pattern Мартин Фаулер](https://martinfowler.com/articles/injection.html) From 47d0ca596a8c7851f89e9b806255a28bbb0f3ddd Mon Sep 17 00:00:00 2001 From: Arduanov Danil Date: Sun, 4 Feb 2024 17:58:51 +0500 Subject: [PATCH 4/4] Fix typos --- guide/ru/concept/di-container.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guide/ru/concept/di-container.md b/guide/ru/concept/di-container.md index 12688e6..4c9a841 100644 --- a/guide/ru/concept/di-container.md +++ b/guide/ru/concept/di-container.md @@ -2,7 +2,7 @@ ## Внедрение зависимостей -В ООП существует два способа повторного использования кода: наследование и композиция. +В ООП существует два способа повторного использования кода: наследование и композиция. Наследование - это просто: @@ -206,4 +206,4 @@ class MyController ## Полезные ссылки -- [Inversion of Control Containers and the Dependency Injection pattern Мартин Фаулер](https://martinfowler.com/articles/injection.html) +- [Inversion of Control Containers and the Dependency Injection pattern Мартина Фаулера](https://martinfowler.com/articles/injection.html)