В чем смысл создания абстракций с одной реализацией в C#?
Что проверяют
Умеете ли вы объяснить, зачем интерфейс IOrderService рядом с единственным OrderService, и готовы ли признать, когда он лишний. Следом обычно спрашивают, как бы вы тестировали класс без интерфейса и не противоречит ли такая абстракция YAGNI.
Как отвечать
Одна реализация в продакшене не значит одна реализация вообще. Абстракция оправдана, когда проводит границу:
- граница с внешним миром: время, сеть, файловая система, платёжный шлюз. Для времени в .NET 8 появился абстрактный
TimeProvider, и тесты подменяют его вместоDateTime.UtcNow. Вторая реализация здесь это фейк в тестах; - инверсия зависимостей между слоями: интерфейс репозитория живёт в доменном проекте, реализация на EF Core в инфраструктурном, и домен не ссылается на инфраструктуру;
- точка расширения: декоратор для кэширования, логирования или ретраев оборачивает сервис без правки его кода, в стандартном DI это делают через Scrutor и
Decorate; - публичный контракт библиотеки: потребители зависят от интерфейса, а реализацию можно менять без ломающих изменений.
Затем назовите цену. Интерфейс на каждый класс удваивает число файлов, переход к определению ведёт в пустой контракт, а сигнатуру приходится менять в двух местах. Если класс не ходит наружу и детерминирован, его проще тестировать живым. Для кода поверх базы интеграционный тест на Testcontainers часто честнее мока репозитория.
Про производительность: вызов через интерфейс виртуальный, но в .NET 8 по умолчанию включён Dynamic PGO, который девиртуализирует горячие вызовы, так что аргументом это бывает редко.
Где спотыкаются
Рассказывают про IShape с Circle и Square. Это полиморфизм с несколькими реализациями, а вопрос именно про одну.
Отвечают догмой «интерфейс нужен всегда, иначе не замокать». Moq и NSubstitute мокают и классы с virtual-методами, а многому коду моки вообще не нужны.
Делают интерфейс слепком класса: все публичные методы подряд, наружу торчат IQueryable и сущности EF. Реализация от этого не прячется, и вторую написать всё равно нельзя.