Dependency Injection (DI) — это паттерн проектирования, который позволяет передавать зависимости в класс извне, а не создавать их внутри. Это способствует слабой связанности и улучшает тестируемость кода.
Основные принципы DI
- Инверсия управления (IoC) — принцип, согласно которому управление зависимостями передается из объекта во внешнюю систему. Это уменьшает связность компонентов и делает код более модульным.
- Разделение интерфейсов и реализации — код работает с абстракциями, а не с конкретными реализациями.
- Контейнеры DI — инструменты для автоматического управления зависимостями.
Реализация Dependency Injection в C#
Внедрение через конструктор
Наиболее распространенный способ внедрения зависимостей:
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class Service
{
private readonly ILogger _logger;
public Service(ILogger logger)
{
_logger = logger;
}
public void Execute()
{
_logger.Log("Executing service...");
}
}
Использование DI-контейнера в .NET
В .NET встроен контейнер для управления зависимостями. Рассмотрим его использование в ASP.NET Core.
Регистрация зависимостей
В файле Program.cs
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<ILogger, ConsoleLogger>();
builder.Services.AddTransient<Service>();
var app = builder.Build();
Разрешение зависимостей
Внедрение в контроллер или сервис:
public class HomeController
{
private readonly Service _service;
public HomeController(Service service)
{
_service = service;
}
public void Run()
{
_service.Execute();
}
}
Жизненный цикл зависимостей
При регистрации зависимостей в контейнере DI необходимо учитывать их жизненный цикл. В .NET предусмотрены три основных типа:
- Transient — создается новый экземпляр объекта при каждом запросе.
builder.Services.AddTransient<IMyService, MyService>();
- Scoped — один экземпляр объекта используется в пределах одного запроса (например, в ASP.NET Core).
builder.Services.AddScoped<IMyService, MyService>();
- Singleton — один экземпляр объекта используется в течение всего времени работы приложения.
builder.Services.AddSingleton<IMyService, MyService>();
Выбор подходящего типа зависит от контекста использования зависимостей.
Преимущества Dependency Injection
- Упрощает тестирование с помощью подменяемых зависимостей (Mock-объектов).
- Улучшает поддержку и расширяемость кода.
- Способствует принципам SOLID, особенно SRP и DIP.