Что такое Inversion of Control (IoC) и Dependency Injection (DI)?

«Что такое Inversion of Control (IoC) и Dependency Injection (DI)?» — вопрос из категории Архитектура, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Inversion of Control (IoC) — это архитектурный принцип, при котором управление созданием объектов и потоком выполнения программы инвертируется и передается внешнему контейнеру или фреймворку. Вместо того чтобы класс сам создавал свои зависимости, он их получает извне.

Dependency Injection (DI) — это конкретный шаблон проектирования, реализующий IoC, при котором зависимости объекта (сервисы) "внедряются" в него извне, обычно через конструктор, свойство или метод.

Пример DI в C# (внедрение через конструктор):

// 1. Интерфейс зависимости
public interface IEmailService
{
    void SendEmail(string to, string body);
}

// 2. Конкретная реализация
public class SmtpEmailService : IEmailService
{
    public void SendEmail(string to, string body) { /* ... */ }
}

// 3. Класс-клиент, который получает зависимость извне
public class OrderProcessor
{
    private readonly IEmailService _emailService;

    // Зависимость внедряется через конструктор
    public OrderProcessor(IEmailService emailService)
    {
        _emailService = emailService; // Контроль инвертирован: объект не создает сервис сам
    }

    public void Process(Order order)
    {
        // Логика обработки заказа
        _emailService.SendEmail(order.CustomerEmail, "Your order is processed.");
    }
}

Преимущества использования DI:

  • Слабая связанность: Классы не зависят от конкретных реализаций, только от абстракций (интерфейсов).
  • Удобство тестирования: Зависимости легко подменить mock-объектами в unit-тестах.
  • Гибкость и поддерживаемость: Изменение реализации сервиса требует правки только в одном месте — конфигурации DI-контейнера.
  • Управление жизненным циклом: Контейнер может управлять временем жизни объектов (Singleton, Scoped, Transient).

DI-контейнеры (например, встроенный в ASP.NET Core) автоматизируют процесс связывания и внедрения зависимостей.