Введение
Приветствую, товарищи разработчики!
Сегодня мы отправляемся в мир внедрения зависимостей в JavaScript без внешних библиотек.
Приготовьтесь к захватывающему изучению этой мощной техники, пока мы углубляемся в создание кофейни Джейн. Проявив немного прагматизма и немного технической хитрости, мы раскроем секреты внедрения зависимостей, что позволит вам создавать гибкие, масштабируемые и удобные в сопровождении приложения JavaScript.
Программное обеспечение, в отличие от аппаратного обеспечения, должно легко изменяться. Отсюда и название.
Раскрытие сущности внедрения зависимостей
Прежде чем мы углубимся в практическую реализацию, давайте разберемся в сути внедрения зависимостей.
Представьте себе кофейню Джейн, оживленный центр любителей кофеина. Джейн, знаток кофе, понимает, что она не может преуспеть во всех аспектах своего бизнеса в одиночку. Вместо этого ей помогает продавец кофе (Coffee Guru), который предоставляет бобы, и бариста (бариста Боб), который превращает эти бобы в ☕️. Это позволяет Джейн сосредоточиться на том, что у нее получается лучше всего — управлении своим бизнесом и подаче восхитительных чашек кофе своим постоянным клиентам — путем делегирования ответственности за покупку зерен, их обжарку и, наконец, помол их для приготовления кофе.
Подобно тому, как Джейн зависит от помощи других в ее бизнесе, в разработке программного обеспечения ваш код состоит из различных компонентов, объектов и модулей, которые зависят друг от друга для правильной работы.
Настройка сцены
Чтобы начать наше путешествие, мы закладываем основу для нашего приложения. Мы инициализируем простой проект NodeJS с именем coffee-shop
со следующей структурой каталогов. Код для него доступен на https://github.com/vjrngn/coffee-shop
Чтобы выразить кофейню Джейн в коде, мы создадим несколько классов. Класс CoffeeGuru
— наш поставщик. Он обеспечивает все сырье, необходимое для приготовления кофе.
class CoffeeGuru { getCoffeeBeans() { // Fetch the finest coffee beans } getWater() { // Retrieve purified water } getMilk() { // Procure fresh milk } }
Класс Barista
отвечает за фактическое приготовление кофе.
class Barista { brewCoffee() { // ... makes amazing coffee } }
Наконец, мы создаем класс CoffeeShop
, чтобы собрать все вместе. Давайте на самом деле посмотрим, как они все сочетаются друг с другом.
class CoffeeShop { constructor(barista) { this.barista = barista; } makeCappuccino() { return this.barista.brewCoffee(); } }
Использование возможностей внедрения зависимостей
Джейн использует CoffeeGuru
для получения всех необходимых материалов, поэтому давайте воспользуемся экземпляром CoffeeGuru
, «внеся» его в Barista
.
class Barista { constructor(coffeeGuru) { this.ingredientProvider = coffeeGuru; } brewCoffee() { // ... } }
В коде внедрение означает создание класса с экземпляром CoffeeGuru
. Это известно как внедрение конструктора. Существуют и другие формы внедрения, такие как внедрение метода и внедрение свойства, но наиболее простым и широко используемым является внедрение конструктора.
Так просто! Давайте приготовим горячий, обжигающий капучино!
Мы обновим наши Barista
, чтобы они могли использовать все замечательные бобы, молоко и другие ингредиенты из CoffeeGuru
.
class Barista { constructor(coffeeGuru) { this.ingredientProvider = coffeeGuru; } brewCoffee() { const coffeeBeans = this.ingredientProvider.getCoffeeBeans(); const water = this.ingredientProvider.getWater(); const milk = this.ingredientProvider.getMilk(); // Brew the perfect cup of cappuccino using the ingredients // ... } }
Объединяя все это
Теперь, когда у нас есть наш продавец и бариста, готовые к работе, мы можем начать продавать наш кофе клиентам.
Начнем с создания экземпляра CoffeeGuru
. Затем мы создаем экземпляр Barista
, вставляя в него экземпляр CoffeeGuru
. Наконец, мы создаем наш CoffeeShop
и заставляем его использовать (или внедрять) Barista
.
Благодаря профессиональному руководству кофейного гуру и искусству введения зависимостей Джейн теперь может обслуживать своих клиентов лучшим кофеином.
Фу! Это было МНОГО работы, чтобы служить ☕️.
«Зачем проходить через все эти хлопоты? 🤔», — спросите вы. Хороший вопрос!
Чтобы понять, почему этот метод так эффективен, предположим, что Джейн решает, что ее продавец слишком дорог, и она больше не может покупать у него. В этом надуманном примере это изменение простое. Тем не менее, он демонстрирует очень распространенное явление в разработке программного обеспечения, когда все часто меняется. Например, мы решили использовать Stripe вместо RazorPay для приема платежей клиентов, использовать чистый SQL вместо ORM для выполнения запросов к базе данных и т. д.
Вы очень скоро поймете, что справиться со всем этим без DI становится несостоятельным. Программное обеспечение, в отличие от аппаратного обеспечения, должно легко изменяться. Отсюда и название. DI — это один из методов, который позволяет программному обеспечению быть… «мягким».
Чтобы иметь дело со сменой поставщика, все, что нам нужно сделать, это создать еще один класс, скажем, CoffeeCo
, и использовать его вместо CoffeeGuru
.
Таким образом, нам нужно было только сменить поставщика и ничего не нужно было менять в процессе приготовления кофе! Потрясающе, правда!?🔥
Давайте сделаем быстрый рефакторинг
Самые проницательные из вас заметили бы, что я назвал переменную this.ingredientProvider
в классе Barista
вместо this.coffeeGuru
.
class Barista { constructor(coffeeGuru) { this.ingredientProvider = coffeeGuru; } brewCoffee() { const coffeeBeans = this.ingredientProvider.getCoffeeBeans(); const water = this.ingredientProvider.getWater(); const milk = this.ingredientProvider.getMilk(); // Brew the perfect cup of cappuccino using the ingredients // ... } }
Поскольку Barista
на самом деле не волнует, какой именно поставщик предоставляет bean-компоненты, такое имя нашей переменной позволяет нам ссылаться на поставщика в более абстрактной форме, а не использовать this.coffeeGuru
, поскольку поставщик может измениться в будущем. Теперь мы можем обновить наш класс Barista
следующим образом:
class Barista { constructor(ingredientProvider) { this.ingredientProvider = ingredientProvider; } brewCoffee() { const coffeeBeans = this.ingredientProvider.getCoffeeBeans(); const water = this.ingredientProvider.getWater(); const milk = this.ingredientProvider.getMilk(); // Brew the perfect cup of cappuccino using the ingredients // ... } }
Это изменение незначительное и не влияет на функциональность класса Barista
. Однако, поскольку код больше читается, чем пишется, это усиливает намерение, поскольку зависимость ingredientProvider
ясно указывает, что Barista
нужен поставщик для предоставления ингредиентов, но не заботится о конкретном поставщике, таком как CoffeeGuru
.
Если вы хотите узнать больше о том, как внедрение зависимостей помогает поддерживать и тестировать код, подпишитесь, чтобы получать уведомления о моем следующем посте, где я рассказываю именно об этой концепции.
Если вам понравилась эта статья и вы чему-то научились, не стесняйтесь… проявите любовь с помощью 👏 . Это действительно помогает мне охватить более широкую аудиторию и мотивирует писать больше полезных статей. 🙌🏻
До скорого…