Ответ
Dependency Injection (DI, Внедрение зависимостей) — это архитектурный паттерн, при котором зависимости объекта (сервисы, которые он использует) предоставляются ему извне, а не создаются внутри самого объекта. Это основа принципа Инверсии зависимостей (Dependency Inversion Principle, DIP) из SOLID.
Основные цели и преимущества:
- Снижение связанности (Loose Coupling): Классы зависят от абстракций (интерфейсов), а не от конкретных реализаций. Это делает систему гибче.
- Упрощение тестирования (Testability): Зависимости можно легко подменить mock- или stub-объектами в unit-тестах.
- Улучшение поддерживаемости: Изменение реализации сервиса затрагивает минимальное количество кода (обычно только конфигурацию DI-контейнера).
- Управление жизненным циклом: DI-контейнер (например, в ASP.NET Core) контролирует время жизни объектов (Singleton, Scoped, Transient).
Пример: Без DI vs. С DI
Проблема (Жёсткая связь):
public class OrderProcessor
{
private readonly ILogger _logger = new FileLogger(); // Прямое создание зависимости
public void Process(Order order)
{
_logger.Log("Processing started...");
// Логика обработки
}
}
// Тестировать сложно — всегда будет использоваться FileLogger.
Решение с DI (Внедрение через конструктор):
public interface ILogger { void Log(string message); }
public class FileLogger : ILogger { /*...*/ }
public class ConsoleLogger : ILogger { /*...*/ }
public class OrderProcessor
{
private readonly ILogger _logger;
// Зависимость "внедряется" извне
public OrderProcessor(ILogger logger)
{
_logger = logger;
}
public void Process(Order order) { /*...*/ }
}
// Использование и тестирование:
ILogger logger = new ConsoleLogger(); // Или FileLogger(), или MockLogger
var processor = new OrderProcessor(logger); // Гибко!
processor.Process(order);
// В unit-тесте:
var mockLogger = new Mock<ILogger>();
var processorUnderTest = new OrderProcessor(mockLogger.Object);
// Легко проверить, был ли вызван метод Log
DI в ASP.NET Core встроен и настраивается в Startup.cs или Program.cs:
// Регистрация сервисов в контейнере
services.AddScoped<ILogger, FileLogger>(); // Один экземпляр на область видимости (запрос)
services.AddSingleton<ICacheService, RedisCache>(); // Один экземпляр на всё приложение
services.AddTransient<IEmailSender, SmtpEmailSender>(); // Новый экземпляр каждый раз
// Контейнер автоматически внедрит ILogger в OrderProcessor