Site icon AppTractor

Что такое Внедрение зависимостей (Dependency Injection) и как это использовать в разработке?

Что такое Внедрение зависимостей (Dependency injection, DI)? Согласно Википедии:

Внедрение зависимости — процесс предоставления внешней зависимости программному компоненту. Является специфичной формой «инверсии управления» (Inversion of control, IoC), когда она применяется к управлению зависимостями. В полном соответствии с принципом единственной обязанности объект отдаёт заботу о построении требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму.

Внедрение зависимостей

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

Внедрение зависимостей дает вам следующие преимущества:

Основы внедрения зависимостей

Прежде чем конкретно рассматривать внедрение зависимостей в той или иной платформе, давайте поймем, как работает внедрение зависимостей в общем смысле.

Классы часто требуют ссылок на другие классы. Например, классу Car может потребоваться ссылка на класс Engine. Эти обязательные классы называются зависимостями, и в этом примере класс Car зависит от наличия экземпляра класса Engine для запуска.

У класса есть три способа получить нужный объект:

  1. Класс конструирует нужную ему зависимость. В приведенном выше примере Car создаст и инициализирует собственный экземпляр Engine.
  2. Перехватит его откуда-то еще. Некоторые Android API, такие как методы получения Context и getSystemService(), работают таким образом.
  3. Укажет его как параметр. Приложение может предоставить эти зависимости при создании класса или передать их функциям, которым нужна каждая зависимость. В приведенном выше примере конструктор Car получит Engine в качестве параметра.

Третий вариант — это и есть внедрение зависимостей! При таком подходе вы берете зависимости класса и предоставляете их, а не позволяете экземпляру класса получать их самому.

Вот пример. Без внедрения зависимостей представление Car, которое создает свою собственную зависимость Engine в коде, выглядит следующим образом:

class Car {

    private val engine = Engine()

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.start()
}

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

Как выглядит код с внедрением зависимостей? Вместо того, чтобы каждый экземпляр Car конструировал свой собственный объект Engine при инициализации, он получает объект Engine в качестве параметра в своем конструкторе:

class Car(private val engine: Engine) {
    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val engine = Engine()
    val car = Car(engine)
    car.start()
}

Функция main использует Car. Поскольку Car зависит от Engine, приложение создает экземпляр Engine, а затем использует его для создания экземпляра Car.

Преимущества этого подхода на основе DI:

Есть два основных способа внедрения зависимостей в Android:

class Car {
    lateinit var engine: Engine

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.engine = Engine()
    car.start()
}

Автоматическая инъекция зависимостей

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

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

Dagger — это популярная библиотека внедрения зависимостей для Java, Kotlin и Android, поддерживаемая Google. Dagger упрощает использование DI в вашем приложении, создавая и управляя графом зависимостей для вас. Он обеспечивает полностью статические зависимости и зависимости во время компиляции, решая многие проблемы разработки и производительности решений на основе отражения, таких как Guice.

Альтернативы внедрению зависимостей

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

object ServiceLocator {
    fun getEngine(): Engine = Engine()
}

class Car {
    private val engine = ServiceLocator.getEngine()

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.start()
}

Локатор сервисов отличается от внедрения зависимостей способом потребления элементов. С локатора сервисов классы получают контроль и запрашивают объекты для внедрения а с внедрением зависимостей приложение получает контроль и активно внедряет необходимые объекты.

По сравнению с внедрением зависимостей:

Заключение

Внедрение зависимостей дает вашему приложению следующие преимущества:

Что еще почитать про внедрение зависимостей

Источник

Exit mobile version