Java: инверсия управления (IoC) и внедрение зависимостей (DI)

Java
Java: инверсия управления (IoC) и внедрение зависимостей (DI)

Долгое время я очень расплывчато относился к понятиям Inversion of Control и Dependency Injection, и когда я закрывал глаза и думал об этом, у меня всегда возникало чувство головокружения. Но чтобы стать хорошим Java-инженером, мне понадобилась неделя, чтобы досконально в них разобраться.

01. Герметичная муфта

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

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

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

public class Xiaoer {
	public void saodi() {
		System.out.println("小二我在扫达摩院的地");
	}
}

Код для класса Pharaoh выглядит следующим образом:

public class Laowang {
	public void mingling() {
		new Xiaoer().saodi();
	}
}

Код тестового класса выглядит так:

public class Test {

	public static void main(String[] args) {
		Laowang laowang = new Laowang();
		laowang.mingling();
	}

}

Новое ключевое слово используется в методе смешивания класса Laowang для создания объекта класса Xiaoer — этот вид кода имеет высокую степень связанности и высокие затраты на обслуживание.Почему вы так говорите?

Однажды земля Бодхидхармы снова была грязной, и хозяин Ван подумал о втором монахе, но второй монах пошел практиковать ицзинь-цзин, который должен был подмести пол, старый хозяин подумал о третьем монахе, поэтому Лаован Лэй Пришлось переделывать новую команду, поэтому код стал таким:

public class Xiaosan {
    public void saodi() {
        System.out.println("小三我在扫达摩院的地");
    }
}
public class Laowang {
    public void mingling() {
        new Xiaoer().saodi();
    }

    public void mingling1() {
        new Xiaosan().saodi();
    }
}

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

Председатель Лао Ван чувствовал, что он был выдающимся монахом, и следующий приказ подмести пол был таким хлопотным, и он чувствовал себя очень несчастным.

02. Инверсия контроля

Мы должны придумать, как Фараон будет хозяином, верно?

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

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

public interface Heshang {
    void saodi();
}

Модификация кода второго класса выглядит следующим образом:

public class Xiaoer implements Heshang {

    @Override
    public void saodi() {
        System.out.println("小二我在扫达摩院的地");        
    }

    public boolean isYijinjing() {
        // 星期三的时候小二和尚要练易筋经
        return false;
    }
}

Модификация кода малых трех категорий выглядит следующим образом:

public class Xiaosan implements Heshang {

    @Override
    public void saodi() {
        System.out.println("小三我在扫达摩院的地");        
    }
}

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

public class Laofang {
    public static Heshang getSaodiseng() {
        Xiaoer xiaoer = new Xiaoer();
        if (xiaoer.isYijinjing()) {
            return new Xiaosan();
        }
        return xiaoer;
    }
}

Если старая партия подтвердит, что второй монах практикует Ицзинцзин, она будет называться третьим монахом.

Модификация кода класса Pharaoh выглядит следующим образом:

public class Laowang {
    public void mingling() {
        Laofang.getSaodiseng().saodi();
    }
}

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

public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang();
        laowang.mingling();
    }

}

У Старого Вана теперь больше душевного спокойствия, он просто отдает приказы, а его младший брат Лао Фан отвечает за то, кого следует позвать подметать территорию Института Дхармы.

Метод, который мы придумали для Лао Вана, называется инверсией управления (сокращенно IoC). Это не технология, а идея, которая помогает нам разрабатывать слабосвязанные программы.

Инверсию управления можно разделить на "управление" и "инверсию" по смыслу слов.При управлении надо выяснить субъект и объект, и кто кем управляет, при инверсии надо знать что вращение вперед есть.

Видите ли, в случае жесткой связи, когда Лао Ван отдает приказ, он должен создать зависимый объект (второго монаха или третьего монаха) с помощью нового ключевого слова, а после инверсии управления подметающий монах, которого Лаос Ван ищет, зависит от него. Младший брат, Лао Фанг, отвечает, то есть контроль передается Лао Фану. Это наоборот?

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

Внедрение зависимостей (DI) — это основной способ добиться инверсии управления: зависимый объект B создается во время создания экземпляра класса A, и разные объекты внедряются в разные свойства на основе их типа или имени. Существует около 3 конкретных форм реализации:

1) На основе конструктора. Реализуйте конструктор с определенными параметрами и передайте объект зависимого типа при создании нового объекта.

Модификация кода класса Pharaoh выглядит следующим образом:

public class Laowang {
    private Heshang saodiseng;
    
    public Laowang(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }
    public void mingling() {
       this.saodiseng.saodi();
    }
}

Модификация кода тестового класса выглядит следующим образом:

public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang(new Xiaosan());
        laowang.mingling();
    }

}

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

2) На основе установленного метода. Реализуйте общедоступный метод набора определенного свойства и позвольте внешнему контейнеру вызывать объект зависимого типа.

Модификация кода класса Pharaoh выглядит следующим образом:

public class Laowang {
    private Heshang saodiseng;
    
    public Heshang getSaodiseng() {
        return saodiseng;
    }

    public void setSaodiseng(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }

    public void mingling() {
       this.getSaodiseng().saodi();
    }
}

Модификация кода тестового класса выглядит следующим образом:

public class Test {

    public static void main(String[] args) {
        Laowang laowang = new Laowang();
        Xiaosan xiaosan = new Xiaosan();
        laowang.setSaodiseng(xiaosan);
        laowang.mingling();
    }

}

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

3) На основе интерфейса. Реализация специального интерфейса для внешних контейнеров для внедрения объектов зависимых типов сложнее, чем конструкторы и методы набора, и здесь мы ее пропустим.

Некоторые люди могут приравнять инверсию управления к внедрению зависимостей, но на самом деле они принципиально разные: инверсия управления — это идея, а внедрение зависимостей — форма реализации инверсии управления.

04. Весенний фреймворк

Когда мы поймем концепции инверсии управления и внедрения зависимостей, мы, кстати, сможем взглянуть на известную среду Spring. Инверсия управления лежит в основе Spring Framework во всем. Существует два способа реализации внедрения зависимостей в Spring: метод установки (метод передачи по значению) и метод конструктора (метод ссылки).

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

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>

Во-вторых, мы модифицируем класс Laowang следующим образом:

public class Laowang {
    private Heshang saodiseng;
    
    public Laowang(Heshang saodiseng) {
        this.saodiseng = saodiseng;
    }
    public void mingling() {
       this.saodiseng.saodi();
    }
}

Затем мы создаем файл конфигурации Spring application.xml со следующим содержимым:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="laowang" class="com.cmower.java_demo.ioc.Laowang">
    <constructor-arg ref="saodiseng" />
  </bean>
        
  <bean id="saodiseng" class="com.cmower.java_demo.ioc.Xiaosan" />

</beans>

Через элемент настраиваются два объекта, один размещается у Лаованга, а другой — у младшего монаха.Используя элемент, младший монах используется в качестве параметра построения Лаованга.

После того, как подготовка завершена, давайте протестируем его, пример кода выглядит следующим образом:

public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Laowang laowang = (Laowang) context.getBean("laowang");
        laowang.mingling();
    }

}

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

05. Наконец

в заключении:

1) Инверсия управления — это идея разделения в программной инженерии, передачи управления третьей стороне, и третья сторона решает «внедрить» конкретные зависимые объекты в объект вызывающего класса во время выполнения.

2) Внедрение зависимостей можно использовать как реализацию инверсии управления, передавая переменные экземпляра в объект.

3) Через структуру IoC отношения сильной связи между классом A и классом B могут быть установлены через контейнер во время выполнения, то есть работа по созданию экземпляра B передается контейнеру, а класс A может просто используйте его.

Добро пожаловать в официальную учетную запись «Silent King II» и ответьте на ключевое слово «java» в фоновом режиме, чтобы бесплатно получить «Материалы для продвинутых программистов на Java».