Підрозділ 20.6
DIP — Принцип інверсії залежностей
20.6. DIP — Принцип інверсії залежностей Dependency Inversion Principle DIP — п'ятий і в певному сенсі найвагоміший принцип SOLID, бо він підсумовує і об'єднує всі попередні. Сформулював його Роберт Мартін у 19
20.6. DIP — Принцип інверсії залежностей
Dependency Inversion Principle (DIP) — п'ятий і в певному сенсі найвагоміший принцип SOLID, бо він підсумовує і об'єднує всі попередні. Сформулював його Роберт Мартін у 1994–1996 роках. Принцип складається з двох частин:
- Модулі верхнього рівня не повинні залежати від модулів нижнього рівня. Обидва мають залежати від абстракцій.
- Абстракції не повинні залежати від деталей. Деталі мають залежати від абстракцій.
Щоб зрозуміти ці слова конкретно: «модуль верхнього рівня» — це бізнес-логіка (AppointmentService, PatientService), а «модуль нижнього рівня» — це інфраструктура (SqlRepository, SmtpNotifier, FileLogger). «Деталь» — це конкретна реалізація (SQL, SMTP, CSV). «Абстракція» — це інтерфейс або абстрактний клас, що описує лише що треба зробити, але не як.
Без DIP бізнес-логіка знає про конкретну базу даних, конкретний поштовий шлюз, конкретний файловий формат. Зміна будь-якого з них вимагає змін у бізнес-логіці. Тест бізнес-логіки потребує реального SQL-сервера. Це й є «неправильний напрямок залежностей» — бізнес залежить від деталей.
Інверсія — не означає «перевернути все з ніг на голову». Вона означає: хто диктує умови? Без DIP — деталь (SQL) диктує умови бізнес-логіці. З DIP — бізнес-логіка диктує умови (через інтерфейс), а деталь їх виконує.
Пряма залежність: проблема

Рефакторинг: вводимо абстракцію
Рішення — виразити потреби бізнес-логіки через інтерфейс. Бізнес-логіка тепер залежить від абстракції IAppointmentRepository, а не від конкретного SqlAppointmentRepository. Конкретна реалізація «вводиться» ззовні — через конструктор (Dependency Injection).
Dependency Injection: три способи введення залежностей
DIP визначає принцип («залежте від абстракцій»), а Dependency Injection (DI) — механізм його реалізації. DI — це патерн, що описує, як саме залежність «вводиться» (inject) у клас ззовні. Є три основних способи:
Constructor Injection є загальновизнаним стандартом для обов'язкових залежностей: клас не може існувати без своїх залежностей, тому вони передаються в конструкторі. Property Injection і Method Injection вживаються значно рідше — лише тоді, коли залежність справді необов'язкова або специфічна для одного виклику.
DI-контейнер: автоматичне керування залежностями
У великих системах ланцюжки залежностей стають довгими: AppointmentService → IAppointmentRepository → IDbConnection → ... Вручну конструювати всю ієрархію об'єктів у Program.cs нереально. Для цього існують DI-контейнери — бібліотеки, що автоматично вирішують, яку реалізацію підставити для кожного інтерфейсу.
У .NET стандартний DI-контейнер — Microsoft.Extensions.DependencyInjection. Він реєструє «прив'язки» (interface → implementation) і сам конструює об'єкти з потрібними залежностями:
DIP і тестованість: підроблені залежності
Найпрактичніша перевага DIP — можливість підміняти залежності у тестах. Якщо клас залежить від IAppointmentRepository, а не від SqlAppointmentRepository, тест може передати «підроблений» репозиторій, що повертає заздалегідь задані дані:
Три тести — жодного SQL, жодного файлу, жодної мережі. Кожен тест налаштовує FakeAppointmentStore у потрібний стан і перевіряє лише бізнес-логіку SchedulingService. Це і є головна практична цінність DIP.
Підсумок розділу 20: SOLID як єдина система
Хоча кожен принцип можна вивчати окремо, вони утворюють єдину архітектурну систему: