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

В этой статье исследуется внедрение зависимостей в Javascript с использованием функций высшего порядка и сравнивается с классами. Этот шаблон подходит для любого языка, который поддерживает определения автономных функций.

В оставшейся части статьи предположим, что мы создаем простое гипотетическое приложение с сущностью Course, а в курсе есть Lesson. AWS DynamoDB хранит курс, а S3 сегмент хранит уроки как объекты JSON.

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

Внедрение зависимостей с использованием классов

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

В приведенном выше примере мы определили класс Course, который имеет два метода. courseById получает курс от DynamoDB, а addLesson добавляет данный урок в S3. Мы также ввели documentClient и S3 из aws-sdk.

Теперь, когда у нас есть этот класс с некоторыми методами, давайте посмотрим, как его можно использовать. Чтобы использовать это в нашем приложении, нам сначала нужно создать экземпляр Course. Если мы не используем фреймворк внедрения зависимостей, обычно мы достигаем этого с помощью Factoryclass.

Затем мы можем использовать Factory.createCourse в нашем приложении для создания экземпляра, конечно, а затем вызвать соответствующий метод. Мы могли бы расширить Factory, включив в него другие статические методы для создания экземпляров других сущностей, таких как Lesson.

Теперь, если требование изменится в будущем и нам нужно будет сохранить Course в каком-то другом магазине, тогда мы сможем обновить Factory или Course. Более того, наша бизнес-логика останется нетронутой.

Внедрение зависимостей с использованием функции высшего порядка

Обратите внимание, что в примерах с использованием классов нам всегда нужно иметь экземпляр S3 и DocumentClient, прежде чем мы сможем получить экземпляр Course.

Представьте себе сценарий, в котором у нас есть веб-перехватчик или AWS Lambda Function задание которого --getCourseById. В этом сценарии мы создадим экземпляр S3, хотя мы не будем использовать его в этом запросе.

В реальном приложении может быть гораздо более сложная логика для настройки и удаления каждой зависимости, что может снизить производительность.

Давайте рассмотрим внедрение зависимостей на уровне функций.

Предпосылка этого шаблона основана на функции, которая возвращает другую функцию (функция высшего порядка). Функция высшего порядка принимает все зависимости, которые требуются дочерней функции для выполнения своей работы.

В приведенном выше примере мы создаем функцию с именем makeGetCourseById. Его задача - создать функцию, которую мы можем использовать для получения курса по идентификатору.

Затем мы можем использовать функцию высшего порядка, как указано выше, в коде нашего приложения. Мы можем следовать той же схеме для addLesson.

Идея функции высшего порядка состоит в том, что мы создаем отдельные функции, такие как getCourseById или addLesson, один раз и передаем экземпляр в наше приложение. Если бы мы не использовали компоненты более высокого порядка, нам нужно было бы передавать экземпляр documentClient или s3 каждый раз, когда мы хотим получить курс по идентификатору.

В следующем примере кода показано, как мы можем использовать функцию высшего порядка в AWS Lambda Handler.

Создание сервисов с функциями высшего порядка

Будут сценарии, в которых нам может потребоваться создать набор методов, которые мы можем использовать без инициализации каждого метода. Например, мы хотим присоединить службу с набором методов в контексте GraphQL, чтобы использовать их в преобразователях GraphQL.

Для этого мы можем создать функцию, которая возвращает объект с набором функций.

Затем мы можем использовать его в коде нашего приложения следующим образом. Имейте в виду, что мы создаем экземпляр courseService один раз за выполнение приложения, если это возможно, или даже можем кэшировать его, если необходимо.

Передача методов вокруг

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

Код приложения будет выглядеть следующим образом:

Резюме

Мы изучили два разных способа внедрения зависимостей в Javascript. Один из способов добиться внедрения зависимостей в Javascript - использовать Class и Factory. Другой подход заключается в использовании функций высшего порядка для достижения гранулярного внедрения на функциональном уровне. В зависимости от сценариев использования приложения лучше выбрать один подход и придерживаться его. С функциями высшего порядка относительно просто работать и они более гибкие, чем классы Javascript.

Подписывайтесь на меня в Twitter или просматривайте мои проекты на Github.