Как используешь букву D из SOLID в своих приложениях?

«Как используешь букву D из SOLID в своих приложениях?» — вопрос из категории Архитектура, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Принцип инверсии зависимостей (Dependency Inversion Principle - DIP) применяется для создания гибких и тестируемых систем. Его суть: модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Практическое применение:

  1. Зависимость от интерфейсов, а не от классов:

    // Вместо этого (зависимость от детали):
    public class OrderService
    {
        private readonly SqlOrderRepository _repository; // Прямая зависимость
        public OrderService() => _repository = new SqlOrderRepository();
    }
    
    // Делаем так (зависимость от абстракции):
    public class OrderService
    {
        private readonly IOrderRepository _repository; // Зависимость от интерфейса
        public OrderService(IOrderRepository repository) => _repository = repository;
    }
  2. Внедрение зависимостей (DI): Конкретная реализация (SqlOrderRepository, FileOrderRepository) предоставляется извне (через конструктор, свойство или метод), обычно с помощью DI-контейнера.

    // Регистрация в контейнере (например, в Program.cs)
    builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();
    // Или для тестов:
    builder.Services.AddScoped<IOrderRepository, MockOrderRepository>();
  3. Выгоды:

    • Тестируемость: Легко подменить реальный репозиторий или сервис mock-объектом в юнит-тестах.
    • Гибкость: Замена реализации (например, с SQL на NoSQL) требует изменения только в одном месте — конфигурации DI.
    • Слабая связанность: Классы знают только о контрактах (интерфейсах), а не о конкретных деталях реализации.

Пример из реального проекта: Сервис отправки уведомлений зависит от интерфейса INotificationSender. В зависимости от конфигурации, через DI внедряется EmailNotificationSender, SmsNotificationSender или PushNotificationSender. Это позволяет легко добавлять новые способы отправки, не меняя код основного сервиса.