Является ли dependency inversion из SOLID тем же, что и dependency injection
Что проверяют
Различаете ли вы принцип и приём. Оба термина про зависимости и обычно встречаются вместе, поэтому вопрос точно ловит тех, кто выучил аббревиатуры списком, но не проектировал границы модулей.
Как отвечать
Это разные вещи. Инверсия зависимостей это принцип: модуль верхнего уровня не зависит от модуля нижнего, оба зависят от абстракции, а абстракция не зависит от деталей. Существенная часть, которую чаще всего упускают: абстракция принадлежит верхнему уровню, контракт формулирует потребитель, а реализация подстраивается под него. Инвертируется именно направление зависимости между модулями.
Внедрение зависимостей это техника: объект получает зависимости снаружи, через конструктор, аргумент или атрибут, вместо того чтобы создавать их самому. Она помогает соблюсти принцип, но сама по себе его не обеспечивает: можно передавать снаружи конкретный класс и остаться привязанным к деталям.
from typing import Protocol
class Notifier(Protocol):
def send(self, text: str) -> None: ...
class Report:
def __init__(self, notifier: Notifier) -> None:
self._notifier = notifierПротокол объявлен рядом с потребителем: отправка почтой или в мессенджер реализует его, а подмена в тесте становится обычным объектом вместо патча.
Полезно добавить, что внедрение зависимостей не равно контейнеру: в Python обычно хватает аргументов и фабрик. И что принцип достижим без внедрения, например через фабрику или точки входа плагинов.
Где спотыкаются
Называют соблюдением принципа сам факт передачи зависимости в конструктор. Абстракции в коде нет, замена реализации по-прежнему требует правки потребителя, то есть инверсии не произошло.
Кладут интерфейс рядом с реализацией, в инфраструктурный пакет. Тогда верхний слой импортирует нижний, зависимость идёт в прежнюю сторону, а лишний слой остаётся.
Заводят протокол на каждый класс ради правильности. Абстракция окупается там, где реализация действительно меняется: хранилище, внешний сервис, время, генератор идентификаторов. В остальных местах это только удлиняет путь чтения кода.