open atlas
↑ К треку
Архитектурные паттерны ARCH · 01 · 03

Connascence

Connascence объединяет coupling и cohesion в одном словаре. Статическая connascence — зависимость на этапе компиляции; динамическая — в runtime. Сила, локальность и степень показывают стоимость зависимости и какие рефакторинги снижают её эффективнее всего.

ARCH Middle ◷ 22 min
Уровень
ОсновыJuniorMiddleSenior

Две команды на платформе спорили о рефакторинге. Команда billing хотела вынести логику повторных попыток оплаты в отдельный модуль. Команда заказов возражала: «вы сломаете нашу координацию повторов». Спор зашёл в тупик, потому что ни у одной команды не было общего словаря для того, какой именно вид зависимости существует и насколько она плоха. Оба модуля использовали одну константу повтора (зависимость по имени)? Сервис заказов ожидал, что billing сделает обратный вызов в определённой последовательности (зависимость по порядку выполнения)? Оба модуля должны были согласовывать смысл целочисленного статуса (семантическая зависимость)? Каждый тип зависимости имеет разную стоимость и разный путь рефакторинга — но без словаря для них разговор ходил по кругу. Connascence — это тот словарь.

Что такое connascence

Мейлир Пейдж-Джонс ввёл connascence в 1992 году как объединяющую метрику, поглощающую и coupling, и cohesion в одном словаре. Два компонента connascent, если изменение одного требует соответствующего изменения другого. Фреймворк классифицирует почему это изменение требуется — разделяемое имя, тип, алгоритм, порядок выполнения или момент времени — и оценивает стоимость зависимости по трём измерениям: сила, локальность и степень.

Лестница coupling из урока 01 говорила тебе что разделяется (внутренности, общее состояние, управляющие флаги, структуры данных, примитивы). Connascence говорит почему изменение одного компонента вынуждает изменять другой, и расширяет словарь на runtime-зависимости, которые лестница coupling не охватывает.

Статическая connascence: зависимости на этапе компиляции

Статическая connascence обнаруживается без запуска программы. Её может найти проверка типов или линтер.

Connascence of Name (CoN): два компонента должны согласовывать имя. Billing-движок вызывает order.getConfirmedItems(). Если модуль заказов переименует getConfirmedItems в getApprovedLineItems, billing сломается. CoN — самая слабая статическая connascence: rename-рефактор исправляет её везде автоматически.

Connascence of Type (CoT): два компонента должны согласовывать тип. Billing-движок принимает (order: ConfirmedOrder). Если модуль заказов изменит ConfirmedOrder на ApprovedOrder, тип параметра billing устареет. CoT сильнее CoN: переименование типа требует обновления каждой ссылки, объявляющей тип.

Connascence of Meaning (CoM) (также Connascence of Convention): два компонента должны согласовывать значение значения. Если статус заказа кодируется как целое число (0 = pending, 1 = confirmed, 2 = cancelled) и billing проверяет if (order.status === 1), оба модуля связаны с целочисленным соглашением. Когда модуль заказов добавит новый статус (3 = partially fulfilled), целочисленная проверка billing молча неправильно классифицирует новое состояние. CoM значительно сильнее: coupling невидим компилятору и часто производит молчаливые runtime-сбои вместо ошибок сборки.

Connascence of Position (CoP): два компонента должны согласовывать порядок аргументов. Если billing вызывает общую утилиту как applyDiscount(orderId, percentage, reason), а потом её сигнатуру меняют на applyDiscount(reason, percentage, orderId), вызывающий молча передаёт неправильные значения в каждый параметр — код компилируется, типы могут даже совпадать, но поведение неверное. CoP сильнее CoM: позиционный coupling невидим в исходном коде вызывающего — имя функции не изменилось, типы могут не измениться, однако контракт сдвинулся под вызывающим. Ход ослабления: заменить позиционные аргументы именованным объектом параметров — applyDiscount({ orderId, percentage, reason }) — чтобы место вызова стало независимым от порядка аргументов.

Connascence of Algorithm (CoA): два компонента должны использовать один и тот же алгоритм. Если billing хеширует ID заказов для ключей журнала аудита той же MD5-реализацией, которую модуль заказов использует для их генерации, оба должны оставаться синхронизированными по алгоритму хеширования. CoA сильная: зависимость не выражена ни в каком типе или интерфейсе — она живёт в реализации.

Динамическая connascence: runtime-зависимости

Динамическую connascence нельзя обнаружить анализом — требуется наблюдение за работающей системой.

Connascence of Execution (CoE): два компонента должны выполняться в определённом порядке для корректного поведения. Billing-модуль должен вызвать validateOrder(), затем generateInvoice(), затем triggerPayment() именно в таком порядке, иначе платёжный шлюз отклонит запрос. CoE сильнее всех статических форм: невидима статическому анализу, сбои зависят от порядка вызова.

Connascence of Timing (CoT*): два компонента должны выполняться в пределах временного окна относительно друг друга. Webhook-получатель billing должен обработать подтверждение платежа в течение тридцати секунд после его отправки сервисом заказов, иначе заказ истечёт по таймауту. Это runtime-зависимость по расписанию: зависимость на часах, а не на структуре кода.

Connascence of Values (CoV): два компонента должны согласовывать значение связанных данных в runtime. Если billing-движок и сервис заказов оба поддерживают копию итоговой суммы заказа, они должны соглашаться. Когда billing применяет запоздалую скидку и обновляет свою копию, а сервис заказов не обновляет свою — система имеет несогласованное состояние. Самая опасная форма из динамических.

Connascence of Identity (CoI) (самая сильная): два компонента должны ссылаться на один и тот же объект в памяти или ту же сущность в хранилище. Если billing держит прямую ссылку на тот же объект Order, который мутирует сервис заказов, оба связаны с идентичностью объекта и историей его мутаций. Это runtime-эквивалент content coupling.

Три измерения оценки: сила, локальность, степень

Знать тип connascence недостаточно — нужно ещё знать, насколько она затратна. Три измерения оценивают стоимость:

Сила — основная ось, следующая порядку типов, описанному выше. CoN слабее CoM; CoE слабее CoV. Более сильная connascence дороже в управлении, потому что требует больше координации при изменении любой из сторон, а сбои часто менее заметны.

Локальность измеряет, насколько близки connascent-компоненты. Два класса в одном пакете, разделяющие CoM (соглашение о целочисленных кодах статусов) — это локальная connascence: можно обеспечить code review и легко аудировать. Два сервиса в разных bounded contexts, разделяющие CoM через общую схему событий — это дальняя connascence: каждая сторона может эволюционировать независимо, не заметив, что другая изменила смысл значения. Дальняя connascence той же силы гораздо опаснее локальной.

Степень измеряет, сколько элементов вовлечено. CoN между двумя функциями — точечная зависимость. CoN между широко используемой константой и тридцатью местами вызова — высокая степень: один rename плюс тридцать обновлений. Высокая степень connascence пропорционально усиливает стоимость любого изменения.

Почему это работает

Почему локальность так важна? Потому что практическое правило Пейдж-Джонса: сильная connascence должна быть локальной. Если у тебя connascence of algorithm — два компонента, должные согласовывать одну реализацию хеширования — эта зависимость должна быть внутри одного модуля, а не через границу сервиса. Если она должна пересекать границу — ослабь её: закодируй вывод алгоритма (хеш) как тип (CoT), вместо того чтобы требовать от обеих сторон повторно реализовывать хеш (CoA). Локальность — рычаг, который тянешь, когда нельзя устранить connascence: приближай её внутрь, где одно изменение исправляет всё.

Использование connascence для ранжирования рефакторингов

Сила connascence в том, что она превращает «этот код беспорядочен» в «вот конкретная зависимость для ослабления и вот как». Три правила направляют ранжирование:

1. Сначала ослабляй, потом устраняй. CoM (целочисленные коды статусов) можно ослабить до CoT (типизированный enum) перед полным устранением (событийно-ориентированная коммуникация без общего статуса). Ослабление дешевле устранения и часто достаточно.

2. Вторым — локализуй. Если сильная connascence должна существовать, тяни её внутрь границы одного модуля. Два сервиса, разделяющие CoM, можно рефакторить так, чтобы один сервис владел соглашением и переводил его для другого — теперь только одна сторона держит сильную connascence, а другая видит только CoN (имя метода) или CoT (тип).

3. Сокращай степень. Если нельзя ослабить или локализовать — сокращай число вовлечённых элементов. Замени целочисленный статус типизированной константой, импортируемой по ссылке — теперь существует только одно определение, и все тридцать мест вызова держат CoN, а не CoM.

В B2B-платформе, когда billing и ordering разделяли целочисленное соглашение статусов (CoM, дальняя, степень 30), ранжированный рефакторинг был: ввести enum OrderStatus, импортируемый обеими сторонами (ослаблено до CoT, локализовано в одну общую библиотеку, степень сокращена до одного определения). Тридцать целочисленных проверок становятся тридцатью типизированными сравнениями — компилятор теперь ловит coupling вместо молчаливых runtime-неклассификаций.

lesson.inset.note

Connascence не заменяет лестницу coupling или анализ cohesion — она расширяет их. Лестница coupling быстра для классификации существующей формы границы модуля. Connascence точна для решения, какой рефакторинг делать следующим. Когда два модуля имеют конкретную зависимость, причиняющую боль, словарь connascence говорит точно, какой это вид зависимости, и какое преобразование её снижает.

Викторина

Billing-движок и сервис заказов оба парсят JSON-нагрузку и должны согласовываться, что поле `order_status` может принимать одно из четырёх строковых значений: 'pending', 'confirmed', 'cancelled', 'disputed'. Команда billing добавляет пятое значение 'partially_fulfilled', и сервис заказов начинает его отправлять. Billing молча неправильно обрабатывает новый статус две недели, прежде чем кто-то замечает. Какой тип connascence стал причиной и почему он был невидим?

Викторина

Billing-движок должен вызывать validateOrder(), затем generateInvoice(), затем triggerPayment() именно в таком порядке, иначе платёжный шлюз отклоняет запрос. Новый разработчик меняет порядок двух последних шагов. Баг обнаруживается только в продакшене, когда первая попытка платежа завершается неудачей. Какой тип connascence это?

Викторина

Billing-движок и сервис отчётности разделяют Connascence of Meaning о том, как целые числа статусов платежей отображаются на состояния (0=pending, 1=paid, 2=failed, 3=refunded). Это соглашение используется в 40 местах в обоих сервисах. Устранить connascence полностью в этот спринт невозможно. Используя правила оценки connascence, какова ранжированная последовательность рефакторинга?

Вспомните перед уходом
  1. 01
    В чём разница между статической и динамической connascence? Дай по одному примеру каждой.
  2. 02
    Назови три измерения оценки connascence и объясни, что каждое из них измеряет.
  3. 03
    Какова ранжированная последовательность рефакторинга, когда нельзя устранить connascence за один шаг?
Итог

Connascence — словарь, объединяющий coupling и cohesion. Два компонента connascent, когда изменение одного требует соответствующего изменения другого. Классификация называет почему: Connascence of Name (общий идентификатор), Type (общее объявление типа), Meaning (общее соглашение о значении), Position (общий порядок аргументов), Algorithm (общая реализация), Execution (общая runtime-последовательность), Timing (общее временное окно), Values (общие runtime-данные), Identity (общая ссылка на объект). Первые пять статические — обнаруживаются на этапе компиляции. Последние четыре динамические — обнаруживаются только в runtime, и потому более опасны.

Оценка connascence требует трёх измерений: сила (какой тип: от CoN до CoI), локальность (насколько далеко разделённые компоненты — тот же модуль до разных сервисов) и степень (сколько элементов вовлечено). Стоимость изменения пропорциональна всем трём.

Приоритет рефакторинга: сначала ослабь тип connascence (CoM до CoT — безопасный, механический шаг), затем локализуй её (тяни сильную зависимость внутрь границы одного модуля), затем сократи степень (объедини до одного канонического определения). Эти шаги дешевле полного устранения и часто достаточны для того, чтобы сделать зависимость управляемой. Применённая к лестнице coupling и анализу cohesion из уроков 01 и 02, connascence завершает инструментарий: лестница coupling говорит что разделяется, cohesion говорит что должно быть сгруппировано, а connascence говорит какую конкретную зависимость ослабить следующей.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.