Ответ
Dependency Injection (DI) — это паттерн проектирования, который позволяет передавать зависимости компонентам извне, вместо того чтобы компоненты создавали их самостоятельно. Это ключевой принцип для построения слабосвязанных и легко тестируемых систем.
Основные проблемы, которые решает DI:
- Высокая связанность (Tight Coupling): Без DI компоненты часто создают свои зависимости внутри себя, что делает их тесно связанными с конкретными реализациями. Это затрудняет изменение зависимостей или их замену.
- Низкая тестируемость: Тесно связанные компоненты сложно тестировать изолированно, так как для их работы требуются реальные реализации всех зависимостей. Подмена зависимостей на моки или стабы становится проблематичной.
- Низкая гибкость и расширяемость: Изменение реализации одной зависимости может потребовать модификации множества классов, которые её используют.
- Сложность поддержки и читаемости: Код становится менее модульным, а зависимости не всегда очевидны, что усложняет понимание и поддержку системы.
Пример без DI (высокая связанность):
class Database:
    def query(self) -> str:
        return "Данные из реальной базы данных"
class Service:
    def __init__(self):
        # Service жёстко зависит от конкретной реализации Database
        self.db = Database()
    def get_data(self) -> str:
        return self.db.query()
# Проблема: для тестирования Service всегда нужна реальная DatabaseПример с DI (слабая связанность):
from abc import ABC, abstractmethod
# Абстракция для базы данных
class IDatabase(ABC):
    @abstractmethod
    def query(self) -> str:
        pass
class RealDatabase(IDatabase):
    def query(self) -> str:
        return "Данные из реальной базы данных"
class MockDatabase(IDatabase):
    def query(self) -> str:
        return "Мокированные данные для теста"
class Service:
    def __init__(self, db: IDatabase):
        # Зависимость (IDatabase) инжектируется извне
        self.db = db
    def get_data(self) -> str:
        return self.db.query()
# Использование:
real_service = Service(RealDatabase())
print(f"Реальный сервис: {real_service.get_data()}")
# Тестирование:
mock_service = Service(MockDatabase())
print(f"Мокированный сервис: {mock_service.get_data()}")В примере с DI Service не знает, как создаётся IDatabase, и зависит только от её интерфейса. Это позволяет легко подменять реализации (например, RealDatabase на MockDatabase) без изменения кода Service, значительно улучшая тестируемость, гибкость и модульность системы. DI может быть реализован вручную (как в примере) или с помощью специализированных DI-контейнеров/фреймворков.