Что такое Dependency Injection (DI)?
Это паттерн проектирования, который используется для управления зависимостями в программном обеспечении. Суть паттерна заключается в том, что объект получает свои зависимости извне, а не создаёт их сам. Это способствует созданию более гибкого, тестируемого и поддерживаемого кода.
Принципы
- Инверсия управления (Inversion of Control, IoC)
Принцип, согласно которому объект делегирует управление своими зависимостями внешнему источнику.
- Внедрение зависимостей (Dependency Injection)
Конкретная реализация инверсии управления, при которой зависимости передаются объекту через конструктор, методы или свойства.
Способы внедрения зависимостей
- Внедрение через конструктор
Зависимости передаются объекту через параметры его конструктора.
interface Repository {
doSomething(): void;
}
class ConcreteRepository implements Repository {
doSomething() {
console.log("Repository is doing something!");
}
}
class Service {
private readonly repository: Repository;
constructor(repository: Repository) {
this.repository = repository;
}
performAction() {
this.repository.doSomething();
}
}
// Пример использования
const repository = new ConcreteRepository();
const service = new Service(repository);
service.performAction(); // Выведет: Repository is doing something!
- Внедрение через сеттеры
Зависимости передаются объекту через методы-сеттеры после его создания.
interface Repository {
doSomething(): void;
}
class ConcreteRepository implements Repository {
doSomething() {
console.log("Repository is doing something!");
}
}
class Service {
private repository: Repository | undefined;
setRepository(repository: Repository) {
this.repository = repository;
}
performAction() {
if (!this.repository) {
throw new Error("Repository is not set");
}
this.repository.doSomething();
}
}
// Пример использования
const repository = new ConcreteRepository();
const service = new Service();
service.setRepository(repository);
service.performAction(); // Выведет: Repository is doing something!
- Внедрение через интерфейсы
Зависимости передаются объекту через методы, определённые в интерфейсах.
interface IRepository {
doSomething(): void;
}
class Repository implements IRepository {
doSomething() {
console.log("Doing something...");
}
}
class Service {
private repository: IRepository;
constructor(repository: IRepository) {
this.repository = repository;
}
performAction() {
this.repository.doSomething();
}
}
// Пример использования
const repository = new Repository();
const service = new Service(repository);
service.performAction(); // Выведет: Doing something...
Плюсы
- Улучшение тестируемости
Код становится более модульным и тестируемым, так как зависимости можно легко заменять на моки или стабы в тестах.
- Улучшение поддерживаемости
Уменьшается связность кода, что упрощает его поддержку и модификацию.
- Улучшение гибкости
Легче менять реализации зависимостей, не изменяя код, который их использует.
- Явное указание зависимостей
Зависимости объектов становятся явными, что улучшает понимание кода.
Минусы
- Усложнение кода
Внедрение DI может усложнить код, особенно если используется слишком много уровней абстракции.
- Кривая обучения
Понимание и правильное использование DI может потребовать времени и обучения, особенно для разработчиков, незнакомых с паттерном.
- Перегрузка конструкции
При внедрении большого числа зависимостей через конструктор конструктор может стать перегруженным.
👉 @seniorFront