Ответ
Снижение связанности — это ключевой принцип проектирования, направленный на минимизацию зависимостей между компонентами системы. Цель — сделать модули максимально независимыми, чтобы изменения в одном из них оказывали минимальное влияние на другие.
Проблема высокой связанности (Tight Coupling):
// Класс OrderService жестко зависит от конкретной реализации EmailSender и SqlOrderRepository.
public class OrderService
{
private SqlOrderRepository _repository = new SqlOrderRepository(); // Прямое создание
private EmailSender _emailSender = new EmailSender("smtp.mycompany.com"); // Прямое создание
public void PlaceOrder(Order order)
{
_repository.Save(order);
_emailSender.Send(order.CustomerEmail, "Your order is placed!");
}
}
// Проблемы:
// 1. Невозможно протестировать OrderService изолированно (он отправит реальные письма).
// 2. Замена SMTP-сервера или БД требует изменения кода OrderService.
// 3. Код нельзя переиспользовать в другом контексте.
Решение: низкая связанность (Loose Coupling) через абстракции и Dependency Injection:
// 1. Определяем абстракции (интерфейсы)
public interface IOrderRepository { void Save(Order order); }
public interface INotificationService { void SendNotification(string email, string message); }
// 2. Реализуем конкретные классы, зависящие от абстракций
public class OrderService
{
private readonly IOrderRepository _repository;
private readonly INotificationService _notifier;
// 3. Зависимости внедряются извне (Dependency Injection)
public OrderService(IOrderRepository repository, INotificationService notifier)
{
_repository = repository;
_notifier = notifier;
}
public void PlaceOrder(Order order)
{
_repository.Save(order);
_notifier.SendNotification(order.CustomerEmail, "Your order is placed!");
}
}
// 4. Конкретные реализации могут быть чем угодно
public class SqlOrderRepository : IOrderRepository { /* ... */ }
public class MongoOrderRepository : IOrderRepository { /* ... */ }
public class EmailNotificationService : INotificationService { /* ... */ }
public class SmsNotificationService : INotificationService { /* ... */ }
// 5. Компоновка (например, в Program.cs или Startup.cs)
builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>();
builder.Services.AddScoped<INotificationService, EmailNotificationService>();
builder.Services.AddScoped<OrderService>(); // DI-контейнер сам разрешит зависимости
Ключевые выгоды низкой связанности:
-
Упрощение тестирования (Unit Testing): Можно легко подменить реальные зависимости "заглушками" (mock/stub).
[Test] public void PlaceOrder_Should_Save_And_Notify() { // Arrange var mockRepo = new Mock<IOrderRepository>(); var mockNotifier = new Mock<INotificationService>(); var service = new OrderService(mockRepo.Object, mockNotifier.Object); var testOrder = new Order(); // Act service.PlaceOrder(testOrder); // Assert mockRepo.Verify(r => r.Save(testOrder), Times.Once); mockNotifier.Verify(n => n.SendNotification(It.IsAny<string>(), It.IsAny<string>()), Times.Once); } - Гибкость и расширяемость: Замена реализации (например, переезд с SQL на MongoDB или с email на SMS) требует изменения только в одном месте — конфигурации DI-контейнера.
- Повышение переиспользуемости: Модуль
OrderServiceне знает о деталях внешнего мира, его можно использовать в разных приложениях. - Упрощение понимания и поддержки: Классы имеют четкие, ограниченные обязанности и зависимости.
Основные техники для снижения связанности: Dependency Injection, использование интерфейсов, следование принципам SOLID (особенно Dependency Inversion), применение паттернов (Фасад, Наблюдатель, Медиатор).