Ответ
Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) — это пятый принцип SOLID. Он гласит:
- Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали (конкретные реализации) должны зависеть от абстракций.
Основная цель — уменьшить связанность (coupling) между компонентами системы, что делает ее более гибкой, тестируемой и расширяемой.
Проблема: Прямая зависимость
Модуль высокого уровня (например, PasswordReminder) напрямую зависит от конкретной реализации низкого уровня (MySQLConnection). Если мы захотим сменить базу данных на PostgreSQL, нам придется изменять код PasswordReminder.
class MySQLConnection:
def connect(self):
print("Подключение к MySQL...")
return "данные из MySQL"
class PasswordReminder:
def __init__(self):
# Прямая зависимость от конкретного класса
self.db_connection = MySQLConnection()
def get_users_data(self):
return self.db_connection.connect()
Решение: Инверсия зависимостей через абстракцию
Мы вводим абстрактный интерфейс (DBConnectionInterface). Теперь PasswordReminder зависит не от конкретной базы данных, а от этого интерфейса. Конкретные реализации (MySQLConnection, PostgreSQLConnection) также зависят от этого интерфейса, реализуя его.
from abc import ABC, abstractmethod
# 1. Создаем абстракцию
class DBConnectionInterface(ABC):
@abstractmethod
def connect(self):
pass
# 2. Детали зависят от абстракции
class MySQLConnection(DBConnectionInterface):
def connect(self):
print("Подключение к MySQL...")
return "данные из MySQL"
class PostgreSQLConnection(DBConnectionInterface):
def connect(self):
print("Подключение к PostgreSQL...")
return "данные из PostgreSQL"
# 3. Модуль верхнего уровня зависит от абстракции
class PasswordReminder:
def __init__(self, db_connection: DBConnectionInterface):
# Зависимость передается извне (Dependency Injection)
self.db_connection = db_connection
def get_users_data(self):
return self.db_connection.connect()
# Теперь мы можем легко подменять реализации без изменения PasswordReminder
mysql = MySQLConnection()
reminder_mysql = PasswordReminder(mysql)
reminder_mysql.get_users_data()
postgres = PostgreSQLConnection()
reminder_postgres = PasswordReminder(postgres)
reminder_postgres.get_users_data()
Таким образом, DIP позволяет создавать слабосвязанные компоненты, которые легко заменять и тестировать в изоляции.