Nette Documentation Preview

syntax
Часто задаваемые вопросы об DI (FAQ)
************************************


Является ли DI другим названием для IoC? .[#toc-is-di-another-name-for-ioc]
---------------------------------------------------------------------------

Принцип *Inversion of Control* (IoC) - это принцип, сосредоточенный на способе выполнения кода - инициирует ли ваш код внешний код или ваш код интегрирован во внешний код, который затем вызывает его.
IoC - это широкая концепция, включающая [события |nette:glossary#Events], так называемый [голливудский принцип |application:components#Hollywood style] и другие аспекты.
Фабрики, которые являются частью [правила №3: Let the Factory Handle It |introduction#Rule #3: Let the Factory Handle It], и представляют собой инверсию для оператора `new`, также являются компонентами этой концепции.

*Dependency Injection* (DI) - это то, как один объект знает о другом объекте, т.е. зависимость. Это паттерн проектирования, который требует явной передачи зависимостей между объектами.

Таким образом, можно сказать, что DI является специфической формой IoC. Однако не все формы IoC подходят с точки зрения чистоты кода. Например, к антипаттернам мы относим все техники, работающие с [глобальным состоянием |global state], или так называемый [Service Locator |#What is a Service Locator].


Что такое локатор сервисов? .[#toc-what-is-a-service-locator]
-------------------------------------------------------------

Сервисный локатор - это альтернатива инъекции зависимостей. Он работает путем создания центрального хранилища, в котором регистрируются все доступные сервисы или зависимости. Когда объекту нужна зависимость, он запрашивает ее в Service Locator.

Однако, по сравнению с Dependency Injection, этот метод теряет в прозрачности: зависимости не передаются объектам напрямую и поэтому их нелегко идентифицировать, что требует изучения кода для выявления и понимания всех связей. Тестирование также усложняется, поскольку мы не можем просто передать имитационные объекты тестируемым объектам, а должны пройти через Service Locator. Кроме того, Service Locator нарушает дизайн кода, поскольку отдельные объекты должны знать о его существовании, что отличается от Dependency Injection, где объекты не знают о контейнере DI.


Когда лучше не использовать DI? .[#toc-when-is-it-better-not-to-use-di]
-----------------------------------------------------------------------

Не существует известных трудностей, связанных с использованием паттерна проектирования Dependency Injection. Напротив, получение зависимостей из глобально доступных мест приводит к [ряду осложнений |global-state], как и использование локатора служб.
Поэтому рекомендуется всегда использовать DI. Это не догматический подход, просто лучшей альтернативы пока не найдено.

Однако существуют определенные ситуации, когда мы не передаем объекты друг другу, а получаем их из глобального пространства. Например, при отладке кода и необходимости сбросить значение переменной в определенный момент программы, измерить длительность определенной части программы или записать сообщение в журнал.
В таких случаях, когда речь идет о временных действиях, которые впоследствии будут удалены из кода, вполне законно использовать глобально доступный дампер, секундомер или логгер. Эти инструменты, в конце концов, не относятся к дизайну кода.


Есть ли у использования DI недостатки? .[#toc-does-using-di-have-its-drawbacks]
-------------------------------------------------------------------------------

Имеет ли использование Dependency Injection какие-либо недостатки, такие как увеличение сложности написания кода или ухудшение производительности? Что мы теряем, когда начинаем писать код в соответствии с DI?

DI не влияет на производительность приложения или требования к памяти. Производительность контейнера DI может играть определенную роль, но в случае с [Nette DI | nette-container] контейнер компилируется в чистый PHP, поэтому его накладные расходы во время выполнения приложения практически равны нулю.

При написании кода необходимо создавать конструкторы, принимающие зависимости. Раньше это могло отнимать много времени, но благодаря современным IDE и [продвижению свойств конструкторов |https://blog.nette.org/ru/php-8-0-polnyj-obzor-novostej#toc-constructor-property-promotion] теперь это дело нескольких секунд. Фабрики могут быть легко созданы с помощью Nette DI и плагина PhpStorm всего за несколько кликов.
С другой стороны, нет необходимости писать синглтоны и статические точки доступа.

Можно сделать вывод, что правильно спроектированное приложение с использованием DI не короче и не длиннее по сравнению с приложением, использующим синглтоны. Части кода, работающие с зависимостями, просто извлекаются из отдельных классов и переносятся в новые места, т.е. в контейнер DI и фабрики.


Как переписать унаследованное приложение на DI? .[#toc-how-to-rewrite-a-legacy-application-to-di]
-------------------------------------------------------------------------------------------------

Переход от устаревшего приложения к Dependency Injection может быть сложным процессом, особенно для больших и сложных приложений. Важно подходить к этому процессу систематически.

- При переходе на Dependency Injection важно, чтобы все члены команды понимали используемые принципы и практики.
- Сначала проведите анализ существующего приложения, чтобы определить ключевые компоненты и их зависимости. Составьте план, какие части будут рефакторизоваться и в каком порядке.
- Реализуйте DI-контейнер или, что еще лучше, используйте существующую библиотеку, такую как Nette DI.
- Постепенно рефакторить каждую часть приложения для использования Dependency Injection. Это может включать модификацию конструкторов или методов для принятия зависимостей в качестве параметров.
- Измените места в коде, где создаются объекты зависимостей, так, чтобы вместо этого зависимости внедрялись контейнером. Это может включать использование фабрик.

Помните, что переход на Dependency Injection - это инвестиции в качество кода и долгосрочную устойчивость приложения. Хотя внести эти изменения может быть непросто, результатом должен стать более чистый, модульный и легко тестируемый код, готовый к будущим расширениям и обслуживанию.


Почему композиция предпочтительнее наследования? .[#toc-why-composition-is-preferred-over-inheritance]
------------------------------------------------------------------------------------------------------
Предпочтительнее использовать [композицию |nette:introduction-to-object-oriented-programming#composition] вместо [наследования |nette:introduction-to-object-oriented-programming#inheritance], поскольку она позволяет повторно использовать код, не заботясь о последствиях изменений. Таким образом, обеспечивается более тесная связь, когда не нужно беспокоиться о том, что изменение одного кода приведет к необходимости изменения другого, зависимого кода. Типичным примером является ситуация, называемая " [адом конструкторов |passing-dependencies#Constructor hell]".


Можно ли использовать Nette DI Container вне Nette? .[#toc-can-nette-di-container-be-used-outside-of-nette]
-----------------------------------------------------------------------------------------------------------

Безусловно. Nette DI Container является частью Nette, но разработан как отдельная библиотека, которую можно использовать независимо от других частей фреймворка. Просто установите его с помощью Composer, создайте конфигурационный файл, определяющий ваши сервисы, а затем используйте несколько строк PHP-кода для создания DI-контейнера.
И вы можете немедленно начать использовать преимущества Dependency Injection в своих проектах.

В главе [Nette DI Container |nette-container] описано, как выглядит конкретный пример использования, включая код.


Почему конфигурация находится в файлах NEON? .[#toc-why-is-the-configuration-in-neon-files]
-------------------------------------------------------------------------------------------

NEON - это простой и легко читаемый язык конфигурации, разработанный в Nette для настройки приложений, сервисов и их зависимостей. По сравнению с JSON или YAML, он предлагает гораздо более интуитивно понятные и гибкие возможности для этой цели. В NEON можно естественным образом описать привязки, которые невозможно было бы написать в Symfony & YAML либо вообще, либо только через сложное описание.


Замедляет ли парсинг NEON файлов работу приложения? .[#toc-does-parsing-neon-files-slow-down-the-application]
-------------------------------------------------------------------------------------------------------------

Хотя файлы NEON разбираются очень быстро, этот аспект не имеет особого значения. Причина в том, что разбор файлов происходит только один раз во время первого запуска приложения. После этого код контейнера DI генерируется, сохраняется на диске и выполняется для каждого последующего запроса без необходимости дальнейшего разбора.

Именно так это работает в производственной среде. Во время разработки файлы NEON разбираются при каждом изменении их содержимого, что гарантирует, что у разработчика всегда есть актуальный контейнер DI. Как упоминалось ранее, фактический разбор происходит мгновенно.


Как я могу получить доступ к параметрам из конфигурационного файла в своем классе? .[#toc-how-do-i-access-the-parameters-from-the-configuration-file-in-my-class]
-----------------------------------------------------------------------------------------------------------------------------------------------------------------

Помните о [правиле №1: "Пусть вам переда |introduction#Rule #1: Let It Be Passed to You]дут". Если класс требует информацию из конфигурационного файла, нам не нужно выяснять, как получить доступ к этой информации; вместо этого мы просто запрашиваем ее - например, через конструктор класса. А передачу мы выполняем в конфигурационном файле.

В этом примере `%myParameter%` - это место для значения параметра `myParameter`, который будет передан конструктору `MyClass`:

```php
# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)
```

Если вы хотите передать несколько параметров или использовать автоподключение, полезно [обернуть параметры в объект |best-practices:passing-settings-to-presenters].


Поддерживает ли Nette интерфейс PSR-11 Container? .[#toc-does-nette-support-psr-11-container-interface]
-------------------------------------------------------------------------------------------------------

Nette DI Container не поддерживает PSR-11 напрямую. Однако, если вам нужна совместимость между Nette DI Container и библиотеками или фреймворками, которые ожидают контейнерный интерфейс PSR-11, вы можете создать [простой адаптер |https://gist.github.com/dg/7f02403bd36d9d1c73802a6268a4361f], который будет служить мостом между Nette DI Container и PSR-11.

Часто задаваемые вопросы об DI (FAQ)

Является ли DI другим названием для IoC?

Принцип Inversion of Control (IoC) – это принцип, сосредоточенный на способе выполнения кода – инициирует ли ваш код внешний код или ваш код интегрирован во внешний код, который затем вызывает его. IoC – это широкая концепция, включающая события, так называемый голливудский принцип и другие аспекты. Фабрики, которые являются частью правила №3: Let the Factory Handle It, и представляют собой инверсию для оператора new, также являются компонентами этой концепции.

Dependency Injection (DI) – это то, как один объект знает о другом объекте, т.е. зависимость. Это паттерн проектирования, который требует явной передачи зависимостей между объектами.

Таким образом, можно сказать, что DI является специфической формой IoC. Однако не все формы IoC подходят с точки зрения чистоты кода. Например, к антипаттернам мы относим все техники, работающие с глобальным состоянием, или так называемый Service Locator.

Что такое локатор сервисов?

Сервисный локатор – это альтернатива инъекции зависимостей. Он работает путем создания центрального хранилища, в котором регистрируются все доступные сервисы или зависимости. Когда объекту нужна зависимость, он запрашивает ее в Service Locator.

Однако, по сравнению с Dependency Injection, этот метод теряет в прозрачности: зависимости не передаются объектам напрямую и поэтому их нелегко идентифицировать, что требует изучения кода для выявления и понимания всех связей. Тестирование также усложняется, поскольку мы не можем просто передать имитационные объекты тестируемым объектам, а должны пройти через Service Locator. Кроме того, Service Locator нарушает дизайн кода, поскольку отдельные объекты должны знать о его существовании, что отличается от Dependency Injection, где объекты не знают о контейнере DI.

Когда лучше не использовать DI?

Не существует известных трудностей, связанных с использованием паттерна проектирования Dependency Injection. Напротив, получение зависимостей из глобально доступных мест приводит к ряду осложнений, как и использование локатора служб. Поэтому рекомендуется всегда использовать DI. Это не догматический подход, просто лучшей альтернативы пока не найдено.

Однако существуют определенные ситуации, когда мы не передаем объекты друг другу, а получаем их из глобального пространства. Например, при отладке кода и необходимости сбросить значение переменной в определенный момент программы, измерить длительность определенной части программы или записать сообщение в журнал. В таких случаях, когда речь идет о временных действиях, которые впоследствии будут удалены из кода, вполне законно использовать глобально доступный дампер, секундомер или логгер. Эти инструменты, в конце концов, не относятся к дизайну кода.

Есть ли у использования DI недостатки?

Имеет ли использование Dependency Injection какие-либо недостатки, такие как увеличение сложности написания кода или ухудшение производительности? Что мы теряем, когда начинаем писать код в соответствии с DI?

DI не влияет на производительность приложения или требования к памяти. Производительность контейнера DI может играть определенную роль, но в случае с Nette DI контейнер компилируется в чистый PHP, поэтому его накладные расходы во время выполнения приложения практически равны нулю.

При написании кода необходимо создавать конструкторы, принимающие зависимости. Раньше это могло отнимать много времени, но благодаря современным IDE и продвижению свойств конструкторов теперь это дело нескольких секунд. Фабрики могут быть легко созданы с помощью Nette DI и плагина PhpStorm всего за несколько кликов. С другой стороны, нет необходимости писать синглтоны и статические точки доступа.

Можно сделать вывод, что правильно спроектированное приложение с использованием DI не короче и не длиннее по сравнению с приложением, использующим синглтоны. Части кода, работающие с зависимостями, просто извлекаются из отдельных классов и переносятся в новые места, т.е. в контейнер DI и фабрики.

Как переписать унаследованное приложение на DI?

Переход от устаревшего приложения к Dependency Injection может быть сложным процессом, особенно для больших и сложных приложений. Важно подходить к этому процессу систематически.

  • При переходе на Dependency Injection важно, чтобы все члены команды понимали используемые принципы и практики.
  • Сначала проведите анализ существующего приложения, чтобы определить ключевые компоненты и их зависимости. Составьте план, какие части будут рефакторизоваться и в каком порядке.
  • Реализуйте DI-контейнер или, что еще лучше, используйте существующую библиотеку, такую как Nette DI.
  • Постепенно рефакторить каждую часть приложения для использования Dependency Injection. Это может включать модификацию конструкторов или методов для принятия зависимостей в качестве параметров.
  • Измените места в коде, где создаются объекты зависимостей, так, чтобы вместо этого зависимости внедрялись контейнером. Это может включать использование фабрик.

Помните, что переход на Dependency Injection – это инвестиции в качество кода и долгосрочную устойчивость приложения. Хотя внести эти изменения может быть непросто, результатом должен стать более чистый, модульный и легко тестируемый код, готовый к будущим расширениям и обслуживанию.

Почему композиция предпочтительнее наследования?

Предпочтительнее использовать композицию вместо наследования, поскольку она позволяет повторно использовать код, не заботясь о последствиях изменений. Таким образом, обеспечивается более тесная связь, когда не нужно беспокоиться о том, что изменение одного кода приведет к необходимости изменения другого, зависимого кода. Типичным примером является ситуация, называемая " адом конструкторов".

Можно ли использовать Nette DI Container вне Nette?

Безусловно. Nette DI Container является частью Nette, но разработан как отдельная библиотека, которую можно использовать независимо от других частей фреймворка. Просто установите его с помощью Composer, создайте конфигурационный файл, определяющий ваши сервисы, а затем используйте несколько строк PHP-кода для создания DI-контейнера. И вы можете немедленно начать использовать преимущества Dependency Injection в своих проектах.

В главе Nette DI Container описано, как выглядит конкретный пример использования, включая код.

Почему конфигурация находится в файлах NEON?

NEON – это простой и легко читаемый язык конфигурации, разработанный в Nette для настройки приложений, сервисов и их зависимостей. По сравнению с JSON или YAML, он предлагает гораздо более интуитивно понятные и гибкие возможности для этой цели. В NEON можно естественным образом описать привязки, которые невозможно было бы написать в Symfony & YAML либо вообще, либо только через сложное описание.

Замедляет ли парсинг NEON файлов работу приложения?

Хотя файлы NEON разбираются очень быстро, этот аспект не имеет особого значения. Причина в том, что разбор файлов происходит только один раз во время первого запуска приложения. После этого код контейнера DI генерируется, сохраняется на диске и выполняется для каждого последующего запроса без необходимости дальнейшего разбора.

Именно так это работает в производственной среде. Во время разработки файлы NEON разбираются при каждом изменении их содержимого, что гарантирует, что у разработчика всегда есть актуальный контейнер DI. Как упоминалось ранее, фактический разбор происходит мгновенно.

Как я могу получить доступ к параметрам из конфигурационного файла в своем классе?

Помните о правиле №1: „Пусть вам передадут“. Если класс требует информацию из конфигурационного файла, нам не нужно выяснять, как получить доступ к этой информации; вместо этого мы просто запрашиваем ее – например, через конструктор класса. А передачу мы выполняем в конфигурационном файле.

В этом примере %myParameter% – это место для значения параметра myParameter, который будет передан конструктору MyClass:

# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)

Если вы хотите передать несколько параметров или использовать автоподключение, полезно обернуть параметры в объект.

Поддерживает ли Nette интерфейс PSR-11 Container?

Nette DI Container не поддерживает PSR-11 напрямую. Однако, если вам нужна совместимость между Nette DI Container и библиотеками или фреймворками, которые ожидают контейнерный интерфейс PSR-11, вы можете создать простой адаптер, который будет служить мостом между Nette DI Container и PSR-11.