Ответ
Связность (cohesion) — это метрика, оценивающая, насколько тесно связаны между собой обязанности и функциональность внутри одного модуля (класса, метода, компонента). Высокая связность — один из ключевых принципов хорошего проектирования (наряду со слабой связанностью).
Принцип: Элементы внутри модуля должны быть объединены выполнением одной, четко определённой задачи или ответственности.
Почему высокая связность — это хорошо?
- Упрощает понимание: Класс с одной ответственностью легче понять и изучить.
- Упрощает поддержку: Изменения в одной функциональной области затрагивают только один модуль.
- Повышает переиспользуемость: Чётко выделенный функционал проще использовать в других контекстах.
- Упрощает тестирование: Легче написать сфокусированные модульные тесты.
Примеры:
1. Класс с высокой связностью (функциональная связность):
// Этот класс отвечает только за валидацию email-адресов.
// Все его методы тесно связаны с одной задачей.
public class EmailValidator
{
public bool IsValidFormat(string email)
{
return Regex.IsMatch(email, @"^[^@s]+@[^@s]+.[^@s]+$");
}
public bool IsDomainAllowed(string email, HashSet<string> allowedDomains)
{
var domain = email.Split('@').Last();
return allowedDomains.Contains(domain);
}
public ValidationResult Validate(string email, HashSet<string> allowedDomains)
{
if (!IsValidFormat(email))
return ValidationResult.Fail("Invalid format");
if (!IsDomainAllowed(email, allowedDomains))
return ValidationResult.Fail("Domain not allowed");
return ValidationResult.Success();
}
}
2. Класс с низкой связностью (случайная/логическая связность):
// Этот класс объединяет три несвязанные ответственности.
// Изменение правил логирования не должно касаться класса, работающего с заказами.
public class OrderManager // Плохое название для такого набора методов
{
public void ProcessOrder(Order order) { /* ... */ }
public void SendPromotionalEmail(Customer customer) { /* ... */ } // Отвечает за коммуникацию
public void LogErrorToDatabase(Exception ex) { /* ... */ } // Отвечает за инфраструктуру логирования
}
Рефакторинг: Класс с низкой связностью следует разделить на несколько классов, каждый со своей высокой связностью: OrderProcessor, EmailService, Logger.
Ответ 18+ 🔞
Давай разберём эту тему про связность, а то некоторые её так понимают, что волосы дыбом встают. Представь себе, что ты заходишь на кухню и видишь: в одном углу стоит холодильник, в другом — стиральная машинка, а на плите — дрель заряжается. Ну и как, удобно? С одной стороны, всё в одном месте, не надо бегать по квартире. С другой — хуйня полная, потому что это три разных дела, которые между собой не связаны ни хуя. Вот это и есть низкая связность, когда в одном модуле намешано всего, как в помойном ведре.
А высокая связность — это когда у тебя на кухне всё для готовки: плита, холодильник, посуда, ножи. Всё связано одной целью — пожрать сделать. Понял разницу? Вот и в коде так же.
Зачем это нужно, спросишь? Да затем, что жить с этим потом надо!
- Понять легко. Открыл класс
EmailValidator— и сразу ясно, что он только почту проверяет. Не лезет в базу, не шлёт письма, не танцует ламбаду. Одна работа. - Менять не страшно. Захотел поменять правила валидации — ковыряешь только этот валидатор. Не боишься, что случайно сломаешь логирование заказов, потому что они в другом месте, на своём хуторе.
- Переиспользовать можно. Нашёл красивый валидатор — взял и вставил в пять разных проектов. А если бы он был в одном классе с отправкой смс и расчётом зарплаты, пришлось бы тащить за собой эту гирю.
- Тестировать — одно удовольствие. Подсунул ему почту и проверяй, что возвращает. Никаких лишних зависимостей, которые надо мокать.
Смотри, вот пример, когда всё сделано по уму:
// Этот парень знает своё место — он валидатор почты и нихуя больше.
// Все его методы про одно и то же.
public class EmailValidator
{
public bool IsValidFormat(string email)
{
return Regex.IsMatch(email, @"^[^@s]+@[^@s]+.[^@s]+$");
}
public bool IsDomainAllowed(string email, HashSet<string> allowedDomains)
{
var domain = email.Split('@').Last();
return allowedDomains.Contains(domain);
}
public ValidationResult Validate(string email, HashSet<string> allowedDomains)
{
if (!IsValidFormat(email))
return ValidationResult.Fail("Invalid format");
if (!IsDomainAllowed(email, allowedDomains))
return ValidationResult.Fail("Domain not allowed");
return ValidationResult.Success();
}
}
Видишь? Класс как монолит. Одна задача, одна ответственность. Красота.
А теперь смотри на этого уродца:
// Это классический пример "бога-класса" или "божественного объекта".
// Он и заказ обработает, и письмо спамное шлёт, и ошибки в базу пишет.
// Связность ниже плинтуса.
public class OrderManager // Название вводит в заблуждение, он же не только заказами управляет!
{
public void ProcessOrder(Order order) { /* ... */ }
public void SendPromotionalEmail(Customer customer) { /* ... */ } // При чём тут это? Это ж сервис рассылки!
public void LogErrorToDatabase(Exception ex) { /* ... */ } // А это ваще инфраструктурный код, уёбок!
}
Вот этот класс — настоящий распиздяй. Он берёт на себя всё подряд. Захотишь поменять способ логирования — тебе придётся лезть в менеджер заказов, ёпта! Это как чинить унитаз, а тебе говорят: «Да заодно и проводку в гостиной переложи, раз уж руки испачкал».
Что делать с таким? Да раздробить его, блядь, на три нормальных класса!
OrderProcessor— пусть только заказы обрабатывает.EmailService— пусть письмами занимается.Logger— пусть логи пишет куда надо.
Каждый будет заниматься своим делом, связность у каждого будет высоченная, и жить станет проще. И запомни, чувак: если не можешь описать ответственность класса одним коротким предложением без союза «и» — значит, ты уже накосячил.