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