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

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

1. Введение – зачем необходимо внедрение зависимостей

1.1 Сделать кофемашину

У нас есть кофемашина (CoffeeMaker), которая состоит из помпы (Pump) и нагревателя (Heater).
Процесс приготовления кофе выглядит следующим образом:

打开
没有打开
加热器打开
加热器是否打开
咖啡煮好了
结束
加热器关闭

Во-первых, код обогревателя следующий:

class ElectricHeater{
    
    
    private var heating = false

    fun isHot(): Boolean {
    
    
        return heating
    }

    fun off() {
    
    
        heating = false
    }

    fun on() {
    
    
        println("~ ~ ~ heating ~ ~ ~")
        heating = true
    }
}

Далее идет насос, который представляет собой сифонное устройство, причем устройство имеет противодурацкий механизм, который не дает тупым людям набирать воду, если они забудут ее нагреть:

class Thermosiphon(val heater: ElectricHeater){
    
    
    fun pump() {
    
    
        if(heater.isHot()){
    
    
            println("=> => pumping => =>")
        }
    }
}

Наконец, комбинация насоса и нагревателя дает кофемашину:

class Coffeemaker{
    
    
    private val heater = ElectricHeater()
    private val pump = Thermosiphon(heater)
    
    fun brew(){
    
    
        heater.on()
        pump.pump()
        println(" [_]P coffee! [_]P ")
        heater.off()
    }
}

1.2 Зависимости

Coffeemaker
- heater: ElectricHeater
- pump: Thermosiphon
+brew()
ElectricHeater
- heating: bool=false
+isHot()
+off()
+on()
Thermosiphon
- heater: ElectricHeater
+pump()

Из приведенной вышеUML диаграммы классов мы можем знать, что кофемашина зависит от нагревателя и насоса, а насос зависит от нагревателя. Нагреватель и кофемашина могут работать независимо без помпы, но кофемашина и помпа неотделимы от нагревателя. Таким образом, модульные кофемашины высокого уровня полагаются на модульные насосы и нагреватели низкого уровня.
Давайте еще раз взглянем на Coffeemaker этот класс. Когда нам нужна кофемашина, нам нужно одновременно создать новый насос и нагреватель. Таким образом, насос, нагреватель и кофемашина тесно связаны между собой. Какие проблемы это вызовет?

  • Вопрос 1: Любитель, который коллекционирует кофемашины, хочет иметь несколько кофемашин, но не хочет иметь много помп и нагревателей. В этом случае, поскольку каждая кофемашина оснащена собственным насосом и нагревателем, это вызовет проблему. Отходы ресурсов.
  • Вопрос 2: Когда производитель разрабатывает кофемашину, он хочет опробовать поддельный нагреватель и насос, чтобы проверить, нет ли каких-либо ошибок в процессе работы кофемашины. В данном случае сделать это без перемещенияCoffeemakerрецепта (кода) невозможно.
  • Вопрос 3: Однажды нагреватель был улучшен, и метод on был измененopen. Кофемашина должна соответствовать нагревателю и соответствовать требованиям доработка, чтобы быть успешной. .

1.3 Улучшить технологию производства

Хорошо, тогда давайте улучшим процесс производства кофемашины и решим эти проблемы.

1.3.1 Разблокировка зависимостей

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

class Coffeemaker(
        private val heater:ElectricHeater,
        private val pump:Thermosiphon
) {
    
    
    fun brew() {
    
    
        heater.on()
        pump.pump()
        println(" [_]P coffee! [_]P ")
        heater.off()
    }
}

Отлично, теперь коллекционеры кофемашин могут иметь только один нагреватель и насос, но много кофемашин.

class TheManHavingManyCoffeemakers {
    
    
    val heater = ElectricHeater()
    val pump = Thermosiphon(heater)
    val cofeemaker1 = Coffeemaker(heater,pump)
    val cofeemaker2 = Coffeemaker(heater,pump)
    ...
}

1.3.2 Инверсия зависимостей

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

interface Heater{
    
    
    fun on()
    fun off()
    fun isHot():Boolean
}

interface Pump{
    
    
    fun pump()
}

Рецепт кофемашины изменяется на следующий процесс:

class Coffeemaker(
    private val heater: Heater, 
    private val pump: Pump
){
    
    
    fun brew(){
    
    
        heater.on()
        pump.pump()
        println(" [_]P coffee! [_]P ")
        heater.off()
    }
}

Здесь все внешние детали должны соответствовать спецификациям (interface), прежде чем их можно будет приобрести для производства кофемашины, чтобы любой производитель мог производить помпы и нагреватели и использовать их в производстве. кофемашин, не нужно беспокоиться о ElectricHeater или других нагревателях, например, его не проблема заменить на: MagicHeater.

class MagicHeater : Heater {
    
    
    private var heating = false

    override fun isHot(): Boolean {
    
    
        return heating
    }

    override fun off() {
    
    
        heating = false
    }

    override fun on() {
    
    
        println("~ ~ ~ This is Magic ~ ~ ~")
        println("~ ~ ~ heating ~ ~ ~")
        println("~ ~ ~ magic ~ ~ ~")
        heating = true
    }
}

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

1.3.3 Улучшить технологию и снизить затраты

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

class PeopleInBeijing {
    
    
    val heater = ElectricHeater()
    val pump = Thermosiphon(heater)
    val cofeemaker = Coffeemaker(heater,pump)
    ...
}

class PeopleInShanghai {
    
    
    val heater = ElectricHeater()
    val pump = Thermosiphon(heater)
    val cofeemaker = Coffeemaker(heater,pump)
    ...
}

...

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

class Injector{
    
    
    private val heater = ElectricHeater()
    private val pump = Thermosiphon(heater)
    
    fun provideHeater() : Heater{
    
    
        return heater
    }
    
    fun providePump() : Pump{
    
    
        return pump
    }
}

class Coffeemaker(injector:Injector)
{
    
        
    private val heater = injector.provideHeater()
    private val pump = injector.providePump()
    
    fun brew(){
    
    
        heater.on()
        pump.pump()
        println(" [_]P coffee! [_]P ")
        heater.off()
    }
}

Когда зависимые детали кофемашины необходимо заменить на детали других производителей или поддельные детали для проверки, достаточно заменить ихInjector. Чтобы пойти еще дальше, машина может сформулировать правила (Интерфейс). Injector

class CoffeeMakerProvider{
    
    
    companion object{
    
    
        fun provideCoffeeMaker() = Coffeemaker(Injector())
    }
}

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

1.3.4 Внедрение технологий и сокращение затрат

Мы изготовили машинуInjector раньше, что улучшило производственный процесс и снизило стоимость. Иногда, несмотря на то, что мы оптимизировали лучше, можно внедрить некоторые более эффективные процессы, такие как платформа внедрения зависимостей. В среде, где используется :Dagger
Kotlin

dependencies {
    
    
...
    implementation 'com.google.dagger:dagger:2.17'
    kapt 'com.google.dagger:dagger-compiler:2.17'
...
}
  1. Внедрить. Сначала мы модифицируем помпу и кофемашину, требующие впрыска, и отмечаем объекты (нагреватели), которые необходимо впрыскивать, @Inject. В Dagger2 @Inject можно использовать для обозначения конструкторов, свойств и функций.

    class Thermosiphon @Inject constructor(val heater: Heater) : Pump {
          
          
        override fun pump() {
          
          
            if(heater.isHot()){
          
          
                println("=> => pumping => =>")
            }
        }
    }
    class CoffeeMaker @Inject constructor(private val heater: Heater, private val pump: Pump) {
          
          
        fun brew(){
          
          
            heater.on()
            pump.pump()
            println(" [_]P coffee! [_]P ")
            heater.off()
        }
    }
    
  2. Модуль. Затем создайте Module, его роль эквивалентна предыдущей роли Injector, отвечающей за производство деталей. Поскольку Thermosiphon также зависит от Heater, насос снабжен не @Provides, а @Binds Link метод предоставления насосу выбранного нами класса насоса (Thermosiphon). Наконец, используйте includes для соединения двух модулей. Dagger одновременно подаст один и тот же нагреватель в кофемашину и накачку.

    @Module(includes = [PumpModule::class])
    class CoffeeMakerModule {
          
          
       @Provides
       fun provideHeater():Heater{
          
          
           return ElectricHeater()
       }
    }
    
    @Module
    abstract class PumpModule{
          
          
        @Binds
        abstract fun providePump(pump: Thermosiphon):Pump
    }
    
  3. Компонент. Наконец, есть линия по производству кофемашин Component, которая очень похожа на предыдущую CoffeeMakerProvider. Все, что нам нужно сделать, это указать Dagger какой модуль использовать и какой тип кофемашины производить.

    @Component(modules = [CoffeeMakerModule::class])
    interface CoffeeComponent {
          
          
        fun provideCoffeeMaker() : CoffeeMaker
    }
    
  4. производит кофемашины. После того, как все будет готово, build этот товар. Dagger2 будет производить для нас компоненты и продукты на основе этих аннотаций.

    fun main(){
          
          
        val coffeeProvider = DaggerCoffeeComponent.builder().build()
        val coffeeMaker = coffeeProvider.provideCoffeeMaker()
        coffeeMaker.brew()
    }
    

Ссылки:
История кофе: внедрение зависимостей, Dagger, Android

Guess you like

Origin blog.csdn.net/qq_31654025/article/details/134988208