В чем разница между Cohesion (связность) и Coupling (связанность)?

«В чем разница между Cohesion (связность) и Coupling (связанность)?» — вопрос из категории Архитектура, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Cohesion (Связность) — это мера того, насколько тесно связаны и сфокусированы на одной задаче обязанности внутри одного модуля (класса, компонента). Coupling (Связанность) — это мера того, насколько сильно один модуль зависит от других модулей.

Цель: Стремиться к высокой связности (High Cohesion) и низкой связанности (Low Coupling). Это фундаментальный принцип проектирования, ведущий к поддерживаемому, тестируемому и гибкому коду.

Высокая связность (High Cohesion)

Класс с высокой связностью делает одну четко определенную вещь. Все его методы и свойства тесно связаны с его основной ответственностью.

Пример класса с высокой связностью:

// Этот класс отвечает только за логирование. Все его методы связаны с этой одной задачей.
public class FileLogger
{
    private readonly string _logFilePath;

    public FileLogger(string logFilePath) => _logFilePath = logFilePath;

    public void LogInfo(string message) => WriteToFile($"[INFO] {DateTime.Now}: {message}");
    public void LogError(string message, Exception ex) => WriteToFile($"[ERROR] {DateTime.Now}: {message} - {ex.Message}");

    private void WriteToFile(string text) => File.AppendAllText(_logFilePath, text + Environment.NewLine);
}

Низкая связанность (Low Coupling)

Модули с низкой связанностью минимально зависят от внутреннего устройства друг друга. Зависимости управляются через абстракции (интерфейсы).

Пример низкой связанности (внедрение зависимости):

// OrderService зависит от абстракции ILogger, а не от конкретной реализации.
// Это снижает связанность и упрощает тестирование.
public class OrderService
{
    private readonly ILogger _logger;
    private readonly IOrderRepository _repository;

    // Зависимости внедряются извне
    public OrderService(ILogger logger, IOrderRepository repository)
    {
        _logger = logger;
        _repository = repository;
    }

    public void ProcessOrder(Order order)
    {
        _logger.LogInfo("Processing order " + order.Id);
        // Логика обработки...
        _repository.Save(order);
    }
}

Пример высокой связанности (плохая практика):

public class OrderService
{
    // Прямое создание зависимостей внутри класса делает его тесно связанным с конкретными реализациями.
    private FileLogger _logger = new FileLogger("log.txt");
    private SqlOrderRepository _repository = new SqlOrderRepository();
    // ...
}

Итог: Высокая связность и низкая связанность делают код более модульным, что упрощает его понимание, изменение, повторное использование и модульное тестирование.