Что такое принцип инверсии зависимостей
Что проверяют
Различаете ли вы принцип и способ его реализации. DIP про направление зависимостей между модулями, а передача зависимости в конструктор это лишь один из способов его получить. В C++ проверяют дополнительно: понимаете ли вы, что абстракция не обязана быть виртуальным интерфейсом и стоить вызова через таблицу.
Как отвечать
Две части принципа: модули верхнего уровня не зависят от модулей нижнего уровня, оба зависят от абстракций; абстракции не зависят от деталей, детали зависят от абстракций. Ключевое следствие часто пропускают: абстракцию объявляет и владеет ею потребитель, а не поставщик. Интерфейс, слово в слово повторяющий один конкретный класс, принципа не даёт.
Динамический вариант:
class Clock {
public:
virtual ~Clock() = default;
virtual std::chrono::system_clock::time_point now() const = 0;
};
class Scheduler {
public:
explicit Scheduler(const Clock& clock) : clock_(clock) {}
private:
const Clock& clock_;
};Статический вариант тот же по смыслу и без накладных расходов: Scheduler как шаблон по типу часов, требования к типу заданы концептом. Выбор между ними сводится к вопросу, нужна ли подмена реализации в рантайме.
Где спотыкаются
Забывают виртуальный деструктор у полиморфной базы. С shared_ptr обойдётся, потому что удалитель запомнен по конкретному типу, а с unique_ptr на базовый тип это неопределённое поведение, и на собеседовании по C++ такое замечают сразу.
Ставят знак равенства между DIP и контейнером внедрения зависимостей. Контейнер это инструмент сборки объектов, он никак не мешает верхнему слою включить заголовок нижнего и зависеть от него на этапе компиляции.
Заводят интерфейс на каждый класс. Абстракция оправдана там, где действительно есть вторая реализация или нужна подмена в тестах; в остальных местах она добавляет косвенность, мешает встраиванию и удлиняет сборку.