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

Ответ

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();
    // ...
}

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

Ответ 18+ 🔞

Давай разберем эту тему, как будто объясняю за пивом, а не на скучной лекции. Представь, что код — это твоя квартира.

Связность (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);
}

Видишь? Никаких посторонних движений. Не пытается заодно и заказ в базу сохранить. Просто пишет в файл. Красота.

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

Пример низкой связанности, умно:

// OrderService не парится, кто там конкретно логирует или сохраняет. Ему похуй, лишь бы контракт соблюдали.
public class OrderService
{
    private readonly ILogger _logger;
    private readonly IOrderRepository _repository;

    // Подсовываем ему зависимости снаружи, как в USB-порт
    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();
    // ...
}

Это как встроить микроволновку прямо в стену на бетон. Захотел новую — долби перфоратором. Заебёшься.

Суть в чём:
Стремись к высокой связности (чтобы класс делал одно дело, а не всё подряд, как швейцарский нож, которым только гвозди выдирают) и к низкой связанности (чтобы модули общались через интерфейсы, а не цеплялись друг за друга, как пьяные друзья).

Итог: когда связность высокая, а связанность низкая, код живёт долго и счастливо. Его легче тестировать (подсовываешь заглушки), легче менять (вырвал один модуль — вставил другой) и понимать (не нужно держать в голове всю вселенную). В общем, красота, а не жизнь.