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

Некоторым людям трудно понять или объяснить внедрение зависимостей — я был одним из них. Я думаю, это в основном из-за вводящего в заблуждение названия, которое вызывает неправильные ассоциации о том, что такое внедрение зависимостей. При «внедрении зависимостей» вы внедряете не зависимость, а объект. Вы также не удаляете зависимость, а вместо этого удаляете зависимость от объекта класса. Но вы также не внедряете объект класса, потому что, если внедрение зависимостей сделано правильно, класс остается неизвестным для клиента.
Допустим, у вас есть следующий код:
public class MyClient {
MyClass myDependency = new MyClass();
}
MyClient и MyClass тесно связаны. Чтобы отделить его, вы можете заменить класс MyClass интерфейсом:
interface IMyClass {}
public class MyClass implements IMyClass {}
public class MyClient {
IMyClass myDependency = new MyClass();
}
Однако у вас все еще есть конструктор MyClass в MyClient . Чтобы также отделить класс от этой зависимости, мы будем использовать внедрение зависимостей. В зависимости от того, как вы хотите внедрить объект (в данном случае объект MyClass), существует 3 различных вида внедрения зависимостей.
Внедрение конструктора
Зависимость передается в конструктор
public class Client {
IMyClass myDependency;
Client (IClass theDependency) {
myDependency = theDependency;
}
}
Часто это самый распространенный способ использования DI.
Преимущества:
- самый элегантный способ реализации DI
- если зависимость важна, вы ее не забудете, потому что она нужна конструктору
Недостаток:
- он негибкий, т.е. если вы хотите добавить другую зависимость, вам нужно написать еще один конструктор. Для каждой новой комбинации вы должны написать другой конструктор
Внедрение сеттера
С Setter Injection зависимость вводится с помощью метода установки:
public class Client {
IMyClass myDependency;
//...
void setDependency(IMyClass theDependency) {
myDependency = theDependency;
}
}
Преимущество:
- в отличие от внедрения конструктора, вы получаете возможность легко добавлять новые зависимости к вашему клиенту, создавая новый метод установки
Недостатки
- за гибкость приходится платить: вы можете забыть добавить зависимость. Если вы его не добавите, то в какой-то момент вы получите исключение Null Pointer.
- сеттеры могут выполняться несколько раз, поэтому вы можете непреднамеренно изменить зависимость с течением времени.
Внедрение интерфейса
В Interface Injection вы создаете интерфейс с методом внедрения зависимости. В принципе, как в Setter Injection, но спецификация перенесена в интерфейс:
interface MyClassInjected {
void injectClass(IMyClass);
}
public class Client implements MyClassInjected {
IMyClass myDependency;
//...
@Override
void injectClass(IMyClass theDependency) {
myDependency = theDependency;
}
}
Преимущество:
- Клиенту не нужно знать интерфейс класса, а только интерфейс для создания зависимости
Недостаток:
- недостатки аналогичны недостаткам Setter Injection: вы можете забыть либо внедрить зависимость, либо случайно внедрить другую зависимость в разное время и, таким образом, перезаписать зависимость
Имейте в виду, однако, DI не золотая пуля. При использовании внедрения зависимостей существуют ограничения. В последней части я объясню вам, в каких случаях нельзя использовать DI:
- Условный: если зависимость привязана к условию, вы не можете использовать DI, потому что вышеуказанные методы не могут узнать, выполняется условие или нет.
- Момент времени: вам нужно знать конкретный момент времени вне клиента, когда нужно назначить зависимость, например. при инициализации или когда устанавливать Setter Injection. Если это не детерминировано, то использовать DI невозможно
- Временные переменные. Если переменная является временной, ее значение определяется логикой программирования. Так что нет смысла вводить его извне
Надеюсь, моя статья немного прояснила для вас ситуацию. У вас есть что добавить?