Ответ
Зацепление (Coupling) — это мера зависимости между модулями, классами или компонентами системы. Высокое зацепление означает, что изменения в одном модуле с высокой вероятностью потребуют изменений в других модулях, что усложняет поддержку, тестирование и модификацию кода.
Уровни зацепления (от худшего к лучшему):
- Content Coupling (содержательное) — один модуль напрямую изменяет внутренние данные другого.
// АНТИПАТТЕРН class ModuleA { public List<int> InternalData = new(); }
class ModuleB { public void Modify(ModuleA a) { a.InternalData.Clear(); // Прямое изменение внутренних данных } }
2. **Common Coupling (общее)** — модули используют общие глобальные данные.
```csharp
// Проблематично
public static class GlobalState
{
public static int Counter;
}
class ModuleA { public void Increment() => GlobalState.Counter++; }
class ModuleB { public void Decrement() => GlobalState.Counter--; }
-
Control Coupling (управляющее) — один модуль передаёт другому флаг, управляющий его поведением.
// Неидеально class ReportGenerator { public void Generate(bool isDetailed) // Флаг управляет логикой { if (isDetailed) GenerateDetailed(); else GenerateSummary(); } } -
Stamp Coupling (структурное) — модули передают сложные структуры данных, но используют только часть полей.
-
Data Coupling (данное) — модули обмениваются только необходимыми примитивными данными.
// Хорошо class OrderProcessor { public decimal CalculateTotal(decimal price, int quantity, decimal taxRate) { return price * quantity * (1 + taxRate); } }
Как достичь низкого зацепления (Loosely Coupled Design):
1. Dependency Injection (DI) и инверсия зависимостей
// Высокое зацепление
class EmailService
{
private readonly SmtpClient _client = new SmtpClient(); // Жёсткая зависимость
}
// Низкое зацепление
interface INotificationService
{
void Send(string message);
}
class EmailService : INotificationService { /* реализация */ }
class SmsService : INotificationService { /* реализация */ }
class OrderProcessor
{
private readonly INotificationService _notifier;
// Зависимость внедряется извне
public OrderProcessor(INotificationService notifier)
{
_notifier = notifier;
}
public void Process(Order order)
{
// Обработка заказа
_notifier.Send("Order processed"); // Не зависит от конкретной реализации
}
}
2. Закон Деметры (Principle of Least Knowledge)
// Нарушение
var price = order.Customer.Address.City.GetTaxRate(); // Цепочка вызовов
// Соблюдение
var taxRate = taxService.GetRateForOrder(order); // Один уровень абстракции
3. Event-Driven Architecture Модули общаются через события, не зная друг о друге.
public class OrderCreatedEvent
{
public Guid OrderId { get; set; }
public DateTime CreatedAt { get; set; }
}
// Publisher
eventBus.Publish(new OrderCreatedEvent { OrderId = order.Id });
// Subscribers (независимые)
class InventoryService : IEventHandler<OrderCreatedEvent>
class NotificationService : IEventHandler<OrderCreatedEvent>
4. Использование интерфейсов и абстракций Зависите от абстракций, а не от конкретных реализаций.
Преимущества низкого зацепления:
- Тестируемость: Модули можно тестировать изолированно
- Поддерживаемость: Изменения локализованы
- Гибкость: Легко заменять реализации
- Повторное использование: Модули независимы
Практическое правило: Стремитесь к такой архитектуре, где можно заменить реализацию модуля, изменив только конфигурацию DI-контейнера, а не код зависимых модулей.