Внедрение зависимостей часто используется для написания слабо связанного кода, который, таким образом, легче тестировать. Когда мы используем внедрение зависимостей в нашем коде, мы, по сути, передаем объекту его переменные экземпляра. Это означает, что вместо того, чтобы возлагать на наш объект ответственность за создание собственных зависимостей, мы вместо этого внедряем зависимости в объект.

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

class LibraryViewModel {
     var library = Library()
}

В приведенном выше коде у нас есть LibraryViewModel, который несет и создает экземпляр библиотеки свойств, тем самым создавая ее собственную зависимость.

class LibraryViewModel {
     var library = Library?
}
let libraryViewModel = LibraryViewModel()
let library = Library()
libraryViewModel.library = library

Здесь есть тот же объект модели представления, но теперь он больше не отвечает за создание собственной зависимости. В этом конкретном примере зависимость вводится в LibraryViewModel через свойство библиотеки и получает ее извне. Такой вид внедрения зависимостей называется внедрением свойств.

Другой способ реализовать внедрение зависимостей - через его конструктор или инициализатор. Внедрение конструктора - это еще один тип внедрения зависимостей, при котором зависимость внедряется в объект прямо в его начале - инициализаторе.

class LibraryViewModel {
     var library: Library
     init(library: Library) {
          self.library = library
     }
}

Здесь мы видим, что наша зависимость находится в конструкторской части кода и отвечает за инициализацию всего нашего LibraryViewModel через это свойство.

В iOS этот тип внедрения часто предпочтительнее, потому что он не позволяет нам использовать объект, который не полностью настроен или имеет какие-либо недостающие части.

Затем у нас есть внедрение метода, и если вы предполагаете, что именно сюда мы внедряем зависимости с помощью наших методов, вы правы! Вот пример внедрения метода в действии.

protocol LibraryManager {
     func deleteAllBooks(in library: Library)
}

Как видите, в нашем LibraryManager протоколе есть метод, который принимает внедренную зависимость.

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

Преимущества внедрения зависимостей:

Разделение проблем

Внедрение зависимостей позволяет нам более четко понимать наш код и разделяет наши проблемы. Когда мы используем внедрение зависимостей, мы видим, что наши объекты отвечают за управление и обработку данной зависимости. Следуя одному из приведенных выше примеров, наш LibraryViewModel отвечает за управление нашим библиотечным объектом и за его обработку.

Тестирование

Ранее в этом посте я упоминал, что внедрение зависимостей отлично подходит для тестирования, и вот почему:

Когда мы тестируем наши объекты, мы можем легко настроить модульные тесты и заменить нашу зависимость другим свойством этого типа. Это позволяет нам создавать фиктивные объекты и изолировать поведение таким образом, чтобы мы могли понять поведение нашего кода. Благодаря внедрению зависимостей наш класс или структура становятся намного более гибкими в различных контекстах, и нам не нужно изменять какой-либо из ее внутреннего кода.

Вот пример внедрения зависимостей, действительно заметный в нашем модульном тесте.

class LibraryManagerTests: XCTestCase {
     func test_GettingLibrary_ShouldReturnBooks() {
          var fakeLibraryViewModel = LibraryViewModel()
          var fakeLibrary = Library()
          fakeLibrary.title = "My Fake Library"
          fakeLibraryViewModel.library = fakeLibrary
          // Some assertion about our viewModel
     }
}

Здесь я создал тестового двойника и вставляю fakeLibrary в наше свойство library в нашем fakeLibraryViewModel. Это помогает мне протестировать поведение этого объекта без использования каких-либо дополнительных фреймворков.

Если у вас есть какие-либо примеры того, как вы используете инъекцию зависимостей в сочетании с протоколами, для создания сети или любыми другими интересными способами, прокомментируйте ниже и поделитесь своим кодом!

Спасибо за прочтение :)