Ответ
Проблема: Жесткая связанность (tight coupling) между высокоуровневым бизнес-сервисом и низкоуровневой реализацией (например, отправкой email). Это усложняет тестирование и замену компонентов.
Решение: Внедрение зависимости (Dependency Injection) через абстракции (интерфейсы).
// 1. Абстракция (контракт)
public interface INotificationService
{
Task SendAsync(string to, string subject, string body);
}
// 2. Конкретная реализация
public class SmtpEmailService : INotificationService
{
public async Task SendAsync(string to, string subject, string body)
{
// Логика отправки через SMTP (например, используя MailKit)
Console.WriteLine($"Отправка email на {to}: {subject}");
await Task.Delay(100); // Имитация асинхронной работы
}
}
// 3. Другая реализация (для тестов или другой инфраструктуры)
public class MockNotificationService : INotificationService
{
public List<string> SentLogs { get; } = new();
public Task SendAsync(string to, string subject, string body)
{
SentLogs.Add($"{to}:{subject}");
return Task.CompletedTask;
}
}
// 4. Высокоуровневый сервис, зависящий от абстракции
public class OrderProcessor
{
private readonly INotificationService _notifier;
// Зависимость внедряется через конструктор
public OrderProcessor(INotificationService notifier)
{
_notifier = notifier;
}
public async Task ProcessOrder(Order order)
{
// ... логика обработки заказа ...
await _notifier.SendAsync(order.CustomerEmail, "Заказ подтвержден", "Ваш заказ принят в работу.");
}
}
// 5. Компоновка (обычно в корне приложения или в DI-контейнере)
class Program
{
static async Task Main()
{
// Выбор конкретной реализации происходит в одном месте
INotificationService notificationService = new SmtpEmailService();
// INotificationService notificationService = new MockNotificationService(); // Для тестов
var processor = new OrderProcessor(notificationService);
await processor.ProcessOrder(new Order());
}
}
Выгоды:
- Тестируемость:
OrderProcessorлегко протестировать, подсунувMockNotificationService. - Гибкость: Замена SMTP-провайдера на другой (например, SendGrid) требует лишь создания новой реализации
INotificationService. - Соблюдение DIP (Принцип инверсии зависимостей): Модули верхнего уровня не зависят от модулей нижнего уровня. Оба зависят от абстракций.