Что означает буква D в принципах SOLID?

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

Ответ

D в SOLID — это Dependency Inversion Principle (DIP), Принцип инверсии зависимостей.

Формулировка:

  1. Модули верхнего уровня (бизнес-логика) не должны зависеть от модулей нижнего уровня (детали реализации: база данных, файловая система, API). Оба должны зависеть от абстракций (интерфейсов или абстрактных классов).
  2. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Пример нарушения DIP:

// Модуль верхнего уровня (OrderService) жестко зависит от модуля нижнего уровня (SqlRepository).
public class SqlRepository
{
    public void Save(Order order) { /* Сохранение в SQL БД */ }
}

public class OrderService // Модуль верхнего уровня
{
    private readonly SqlRepository _repository; // Прямая зависимость от детали

    public OrderService()
    {
        _repository = new SqlRepository(); // Создание зависимости внутри класса (hard-coded)
    }

    public void ProcessOrder(Order order)
    {
        // ... бизнес-логика ...
        _repository.Save(order);
    }
}

Пример с применением DIP и внедрением зависимостей (DI):

// 1. Определяем абстракцию, от которой будут зависеть оба модуля.
public interface IOrderRepository
{
    void Save(Order order);
}

// 2. Деталь реализации зависит от абстракции.
public class SqlRepository : IOrderRepository
{
    public void Save(Order order) { /* Сохранение в SQL БД */ }
}

public class FileRepository : IOrderRepository
{
    public void Save(Order order) { /* Сохранение в файл */ }
}

// 3. Модуль верхнего уровня также зависит только от абстракции.
public class OrderService
{
    private readonly IOrderRepository _repository;

    // Зависимость внедряется извне (через конструктор) - это Inversion of Control.
    public OrderService(IOrderRepository repository)
    {
        _repository = repository;
    }

    public void ProcessOrder(Order order)
    {
        // ... бизнес-логика ...
        _repository.Save(order);
    }
}

Преимущества DIP:

  • Гибкость и расширяемость: Легко заменить реализацию (например, SqlRepository на FileRepository или мок для тестов).
  • Тестируемость: Возможность подмены зависимостей mock-объектами для модульного тестирования.
  • Снижение связанности: Модули системы становятся независимыми от конкретных деталей реализации.