2024-09-27 20:25:32 +03:00
---
aliases:
- DIP
- Принцип инверсии зависимостей
tags:
- maturity/🌱
date: 2024-09-27
zero-link:
- "[[../../meta/zero/00 Архитектура ПО|00 Архитектура ПО]]"
parents:
- "[[SOLID|SOLID]]"
linked:
---
2024-11-23 21:34:40 +03:00
**Принцип инверсии зависимостей (Dependency Inversion Principle, DIP)** гласит: [[высокоуровневые модули]] не должны зависеть от низкоуровневых модулей. О б а должны зависеть от абстракций. Это означает, что вместо использования конкретных реализаций, классы должны работать с абстракциями — интерфейсами или абстрактными классами. DIP делает код более гибким, модульным и легко расширяемым.
2024-09-27 20:25:32 +03:00
2024-11-23 21:34:40 +03:00
**Как соблюдать DIP**
1. **Работайте с абстракциями:** Замените зависимости на интерфейсы или абстрактные классы.
2. **Инвертируйте зависимости:** Используйте внедрение зависимостей ([[Dependency Injection]]) через конструктор, сеттер или контейнеры.
2024-11-24 20:43:38 +03:00
3. Минимизируйте жёсткую [[связанность]]: Высокоуровневые модули должны оставаться независимыми от деталей реализации низкоуровневых модулей.
2024-11-23 21:34:40 +03:00
**Преимущества соблюдения DIP**
1. **Гибкость:** Замена низкоуровневых модулей (реализаций) не требует изменения высокоуровневых модулей.
2. **Улучшенная тестируемость:** Высокоуровневые модули можно тестировать с помощью моков или заглушек, так как они зависят от абстракций.
3. **Снижение связанности:** Высокоуровневый код становится независимым от деталей реализации.
4. **Расширяемость:** Новые реализации интерфейсов можно добавлять без изменений в существующем коде.
## Пример нарушения DIP
Рассмотрим пример с выключателем, который управляет лампой:
2024-09-27 20:25:32 +03:00
```java
public class Lamp {
public void turnOn() {
// Лампа включена
}
}
public class Switch {
private Lamp lamp;
public Switch(Lamp lamp) {
this.lamp = lamp;
}
public void toggle() {
2024-11-23 21:34:40 +03:00
lamp.turnOn(); // Нарушение DIP — жёсткая зависимость от класса Lamp
2024-09-27 20:25:32 +03:00
}
}
```
2024-11-23 21:34:40 +03:00
В данном случае класс `Switch` напрямую зависит от конкретной реализации класса `Lamp` . Если потребуется заменить лампу на другое устройство (например, вентилятор), придётся модифицировать код `Switch` .
Используем интерфейс для абстрагирования устройств:
2024-09-27 20:25:32 +03:00
```java
public interface Switchable {
void turnOn();
}
public class Lamp implements Switchable {
2024-11-23 21:34:40 +03:00
@Override
2024-09-27 20:25:32 +03:00
public void turnOn() {
// Лампа включена
}
}
2024-11-23 21:34:40 +03:00
public class Fan implements Switchable {
@Override
public void turnOn() {
// Вентилятор включен
}
}
```
Теперь `Switch` зависит от интерфейса `Switchable` , а не от конкретного класса:
```java
2024-09-27 20:25:32 +03:00
public class Switch {
private Switchable device;
public Switch(Switchable device) {
this.device = device;
}
public void toggle() {
2024-11-23 21:34:40 +03:00
device.turnOn(); // Зависимость инверсирована — Switch работает с абстракцией
2024-09-27 20:25:32 +03:00
}
}
```
2024-11-23 21:34:40 +03:00
Для изменения устройства достаточно передать новую реализацию интерфейса `Switchable` при создании объекта `Switch` , не модифицируя е г о код.
2024-09-27 20:25:32 +03:00
***
## Мета информация
**Область**:: [[../../meta/zero/00 Архитектура ПО|00 Архитектура ПО]]
**Родитель**:: [[SOLID]]
**Источник**::
**Создана**:: [[2024-09-27]]
**Автор**::
### Дополнительные материалы
-
### Дочерние заметки
<!-- QueryToSerialize: LIST FROM [[]] WHERE contains(Родитель, this.file.link) or contains(parents, this.file.link) -->