---
title: "Связанность (программирование)"
type: "antipattern"
slug: "coupling"
url: "http://localhost:3000/ru/antipatterns/coupling.md"
description: "Степень взаимозависимости между программными модулями"
---
# Связанность (программирование)

> Степень взаимозависимости между программными модулями

В [программной инженерии](https://en.wikipedia.org/wiki/Software%5Fengineering "Software engineering") **связанность** — это степень взаимозависимости между программными [модулями](https://en.wikipedia.org/wiki/Modular%5Fprogramming "Modular programming"), мера того, насколько тесно связаны две подпрограммы или модуля, и сила взаимосвязей между модулями. Связанность не бинарна, а многомерна.

Связанность обычно противопоставляется [связности](https://en.wikipedia.org/wiki/Cohesion%5F%28computer%5Fscience%29 "Cohesion (computer science)"). [Низкая связанность](https://en.wikipedia.org/wiki/Loose%5Fcoupling "Loose coupling") часто коррелирует с высокой связностью, и наоборот. Низкая связанность часто считается признаком хорошо структурированной [компьютерной системы](https://en.wikipedia.org/wiki/Computer%5Fsystem "Computer system") и хорошего проектирования, и в сочетании с высокой связностью способствует достижению общих целей — высокой [читаемости](https://en.wikipedia.org/wiki/Computer%5Fprogramming#Readability%5Fof%5Fsource%5Fcode "Computer programming") и [сопровождаемости](https://en.wikipedia.org/wiki/Maintainability "Maintainability").

## История

[Метрики качества программного обеспечения](https://en.wikipedia.org/wiki/Software%5Fmetric "Software metric") — связанность и связность — были предложены [Ларри Константайном](https://en.wikipedia.org/wiki/Larry%5FConstantine "Larry Constantine") в конце 1960-х годов в рамках [структурного проектирования](https://en.wikipedia.org/wiki/Structured%5Fdesign "Structured design") на основе характеристик «хороших» практик программирования, снижавших затраты на сопровождение и модификацию. Структурное проектирование, включая связность и связанность, было опубликовано в статье _Stevens, Myers & Constantine_ (1974) и книге _Yourdon & Constantine_ (1979), и введённые в последней термины впоследствии стали стандартными.

## Связанность и связность

Связанность и [связность](https://en.wikipedia.org/wiki/Cohesion%5F%28computer%5Fscience%29 "Cohesion (computer science)") — термины, которые очень часто встречаются вместе. Связанность относится к взаимозависимостям между модулями, тогда как связность описывает, насколько связаны между собой функции внутри одного модуля. Низкая связность означает, что данный модуль выполняет задачи, слабо связанные друг с другом, и поэтому может создавать проблемы по мере увеличения размера модуля.

## Степень

Связанность может быть «низкой» (также «[слабой](https://en.wikipedia.org/wiki/Loose%5Fcoupling "Loose coupling")» и «нестрогой») или «высокой» (также «тесной» и «сильной»). Некоторые типы связанности, в порядке убывания от наивысшей к наименьшей, перечислены ниже:

### Процедурное программирование

Под модулем здесь понимается подпрограмма любого рода, то есть набор из одного или нескольких операторов, имеющий имя и, желательно, собственный набор имён переменных.

Связанность по содержимому (высокая)

Связанность по содержимому возникает, когда один модуль использует код другого модуля, например ветвление. Это нарушает [сокрытие информации](https://en.wikipedia.org/wiki/Information%5Fhiding "Information hiding") — базовую концепцию проектирования программного обеспечения.

Общая связанность

Общая связанность возникает, когда несколько модулей имеют доступ к одним и тем же глобальным данным. Однако при внесении изменений это может приводить к неконтролируемому распространению ошибок и непредвиденным побочным эффектам.

Внешняя связанность

Внешняя связанность возникает, когда два модуля совместно используют навязанный извне формат данных, [протокол связи](https://en.wikipedia.org/wiki/Communication%5Fprotocol "Communication protocol") или интерфейс устройства. По сути, это связано со взаимодействием с внешними инструментами и устройствами.

Связанность по управлению

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

Связанность по образцу (связанность по структуре данных)

Связанность по образцу возникает, когда модули совместно используют составную [структуру данных](https://en.wikipedia.org/wiki/Data%5Fstructure "Data structure") и применяют лишь её части, возможно разные (например, передача целой записи в функцию, которой нужно лишь одно её поле).

В этой ситуации изменение поля, которое модулю не нужно, может привести к изменению того, как модуль читает запись. Чтобы проиллюстрировать концепцию связанности по образцу, рассмотрим сценарий с [компонентом](https://en.wikipedia.org/wiki/Software%5Fcomponent "Software component") `UserProfile`. Этот компонент спроектирован так, чтобы в ответ на [запросы](https://en.wikipedia.org/wiki/HTTP "HTTP") возвращать всю информацию профиля пользователя, даже когда [потребителям](https://en.wikipedia.org/wiki/User%5Fagent "User agent") нужен лишь определённый [атрибут](https://en.wikipedia.org/wiki/Attribute%5F%28computing%29 "Attribute (computing)"). Такая практика является примером связанности по образцу, которая может приводить к существенным проблемам с [пропускной способностью](https://en.wikipedia.org/wiki/Bandwidth%5F%28computing%29 "Bandwidth (computing)"), особенно при масштабировании. При изменении любого атрибута внутри компонента `UserProfile` все взаимодействующие с ним потребители могут потребовать [тестирования](https://en.wikipedia.org/wiki/Software%5Ftesting "Software testing"), даже если они не используют изменённый атрибут.

Связанность по данным

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

### Объектно-ориентированное программирование

Связанность подклассов

Описывает отношение между потомком и его родителем. Потомок связан со своим родителем, но родитель не связан с потомком.

Временная связанность

Это когда два действия объединяются в один модуль только потому, что они выполняются в одно и то же время.

В недавних работах исследовались и использовались различные другие концепции связанности в качестве показателей для разных принципов модуляризации, применяемых на практике.

#### Динамическая связанность

Цель определения и измерения этого типа связанности — обеспечить оценку [программной системы](https://en.wikipedia.org/wiki/Software%5Fsystem "Software system") во время выполнения. Утверждается, что статические метрики связанности теряют точность при интенсивном использовании динамического связывания или наследования. В попытке решить эту проблему были учтены меры динамической связанности.

#### Семантическая связанность

Этот вид метрики связанности учитывает концептуальное сходство между программными сущностями, используя, например, комментарии и идентификаторы и опираясь на такие методы, как [латентно-семантическое индексирование](https://en.wikipedia.org/wiki/Latent%5Fsemantic%5Findexing "Latent semantic indexing") (LSI).

#### Логическая связанность

Анализ логической связанности (или эволюционной связанности, или связанности по изменениям) использует историю выпусков программной системы для поиска паттернов изменений среди модулей или классов: например, сущностей, которые, вероятно, будут изменяться вместе, или последовательностей изменений (за изменением класса A всегда следует изменение класса B).

## Измерения связанности

По мнению Грегора Хопе, связанность многомерна:

* Зависимость от технологии
* Зависимость от местоположения
* Зависимость от топологии
* Зависимость от формата и типа данных
* Семантическая зависимость
* Зависимость от диалога
* Зависимость от порядка
* Временная зависимость

## Недостатки сильной связанности

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

1. Изменение в одном модуле обычно вызывает [эффект домино](https://en.wikipedia.org/wiki/Ripple%5Feffect "Ripple effect") — каскад изменений в других модулях.
2. Сборка модулей может требовать больше усилий и/или времени из-за возросшей межмодульной зависимости.
3. Конкретный модуль может быть труднее [повторно использовать](https://en.wikipedia.org/wiki/Code%5Freuse "Code reuse") и/или тестировать, поскольку приходится включать зависимые модули.

## Проблемы производительности

Независимо от того, является ли система слабо или сильно связанной, её производительность часто снижается из-за создания, передачи, преобразования (например, маршалинга) сообщений и параметров, а также интерпретации сообщений (которые могут быть ссылкой на строку, массив или структуру данных), что требует меньше накладных расходов, чем создание сложного сообщения, такого как сообщение [SOAP](https://en.wikipedia.org/wiki/SOAP "SOAP"). Более длинные сообщения требуют больше ресурсов процессора и памяти для формирования. Для оптимизации производительности во время выполнения длину сообщения следует минимизировать, а смысловую нагрузку сообщения — максимизировать.

Накладные расходы на передачу сообщений и производительность

Поскольку сообщение должно передаваться целиком, чтобы сохранить свой полный смысл, передачу сообщений необходимо оптимизировать. Более длинные сообщения требуют больше ресурсов процессора и памяти для передачи и приёма. Кроме того, при необходимости получатели должны собрать сообщение в его исходное состояние, чтобы полностью его принять. Следовательно, для оптимизации производительности во время выполнения длину сообщения следует минимизировать, а смысловую нагрузку — максимизировать.

Накладные расходы на преобразование сообщений и производительность

Протоколы сообщений и сами сообщения часто содержат дополнительную информацию (то есть сведения о пакете, структуре, определениях и языке). Поэтому получателю часто приходится преобразовывать сообщение в более очищенную форму, удаляя лишние символы и сведения о структуре и/или преобразуя значения из одного типа в другой. Любое преобразование увеличивает накладные расходы на процессор и/или память. Для оптимизации производительности во время выполнения форму и содержание сообщения следует сокращать и очищать, чтобы максимизировать его смысл и уменьшить объём преобразований.

Накладные расходы на интерпретацию сообщений и производительность

Все сообщения должны интерпретироваться получателем. Простые сообщения, такие как целые числа, могут не требовать дополнительной обработки для интерпретации. Однако сложные сообщения, такие как сообщения [SOAP](https://en.wikipedia.org/wiki/SOAP "SOAP"), требуют синтаксического анализатора и преобразователя строк, чтобы отобразить заложенный в них смысл. Для оптимизации производительности во время выполнения сообщения следует очищать и сокращать, чтобы минимизировать накладные расходы на интерпретацию.

## Решения

Один из подходов к снижению связанности — [функциональное проектирование](https://en.wikipedia.org/wiki/Functional%5Fdesign "Functional design"), которое стремится ограничить обязанности модулей в соответствии с функциональностью. Связанность между двумя классами `A` и `B` возрастает, если:

* `A` имеет атрибут типа `B` (ссылающийся на него).
* `A` обращается к услугам объекта `B`.
* `A` имеет метод, ссылающийся на `B` (через возвращаемый тип или параметр).
* `A` является подклассом класса `B` (или реализует его).

Низкая связанность подразумевает отношение, при котором один модуль взаимодействует с другим через простой и стабильный интерфейс и не должен заботиться о внутренней реализации другого модуля (см. [сокрытие информации](https://en.wikipedia.org/wiki/Information%5FHiding "Information Hiding")).

Системы, такие как [CORBA](https://en.wikipedia.org/wiki/CORBA "CORBA") или [COM](https://en.wikipedia.org/wiki/Component%5FObject%5FModel "Component Object Model"), позволяют объектам взаимодействовать друг с другом, не зная ничего о реализации другого объекта. Обе эти системы даже позволяют объектам взаимодействовать с объектами, написанными на других языках.

## Связанность и коннасценция

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

[Коннасценция](https://en.wikipedia.org/wiki/Connascence "Connascence"), введённая Мейлиром Пейдж-Джонсом, предоставляет систематическую методику для анализа и измерения зависимостей связанности. Она оценивает зависимости по трём измерениям: сила, которая измеряет усилия, необходимые для рефакторинга или изменения зависимости; локальность, которая учитывает, насколько физически или логически близки зависимые компоненты в [кодовой базе](https://en.wikipedia.org/wiki/Codebase "Codebase"); и степень, которая измеряет, сколько компонентов затрагивает зависимость. Коннасценцию можно разделить на статическую (выявляемую во время компиляции) и динамическую (выявляемую во время выполнения) формы. Статическая коннасценция относится к зависимостям времени компиляции, таким как сигнатуры методов, тогда как динамическая коннасценция относится к зависимостям времени выполнения, которые могут проявляться в таких формах, как коннасценция момента времени, значений или алгоритма.

Каждая разновидность связанности может проявлять несколько типов коннасценции, какой-то конкретный тип или, в редких случаях, не проявлять ни одного — в зависимости от того, как реализована зависимость. Распространённые типы коннасценции включают коннасценцию имени, типа, позиции и смысла. Некоторые типы связанности естественным образом соответствуют конкретным типам коннасценции; например, связанность по данным часто включает коннасценцию имени или типа. Однако не каждое сочетание связанности и коннасценции имеет практический смысл. Зависимости, опирающиеся на порядок параметров в сигнатуре метода, демонстрируют коннасценцию позиции, которая хрупка и трудна для рефакторинга, поскольку изменение порядка параметров нарушает интерфейс. Напротив, коннасценция имени, которая опирается на имена полей или параметров, как правило, более устойчива к изменениям. Сами типы коннасценции образуют естественную иерархию по силе, при этом коннасценция имени обычно считается слабее, чем коннасценция смысла.

Зависимости, пересекающие границы модулей или распределённых систем, обычно имеют более высокие затраты на координацию, что увеличивает сложность рефакторинга и распространения изменений через отдалённые границы. Современные практики, такие как [внедрение зависимостей](https://en.wikipedia.org/wiki/Dependency%5Finjection "Dependency injection") и программирование на основе интерфейсов, часто применяются для снижения силы связанности и улучшения сопровождаемости зависимостей.

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

## Связанность модулей

В программной инженерии связанность описывает один из вариантов метрик, связанных с этой концепцией.

Для связанности по потоку данных и управления:

* d i {\\displaystyle d\_{i}} : число входных параметров данных
* c i {\\displaystyle c\_{i}} : число входных управляющих параметров
* d o {\\displaystyle d\_{o}} : число выходных параметров данных
* c o {\\displaystyle c\_{o}} : число выходных управляющих параметров

Для глобальной связанности:

* g d {\\displaystyle g\_{d}} : число глобальных переменных, используемых как данные
* g c {\\displaystyle g\_{c}} : число глобальных переменных, используемых как управление

Для связанности с окружением:

* w {\\displaystyle w} : число вызываемых модулей (fan-out)
* r {\\displaystyle r} : число модулей, вызывающих рассматриваемый модуль (fan-in)

C o u p l i n g ( C ) \= 1 − 1 d i + 2 × c i + d o + 2 × c o + g d + 2 × g c + w + r {\\displaystyle \\mathrm {Coupling} (C)=1-{\\frac {1}{d\_{i}+2\\times c\_{i}+d\_{o}+2\\times c\_{o}+g\_{d}+2\\times g\_{c}+w+r}}}

`Coupling(C)` даёт тем большее значение, чем сильнее связан модуль. Это число варьируется примерно от 0.67 (низкая связанность) до 1.0 (высокая связанность)

Например, если модуль имеет только один входной и один выходной параметр данных

C \= 1 − 1 1 + 0 + 1 + 0 + 0 + 0 + 1 + 0 \= 1 − 1 3 \= 0.67 {\\displaystyle C=1-{\\frac {1}{1+0+1+0+0+0+1+0}}=1-{\\frac {1}{3}}=0.67}

Если модуль имеет 5 входных и выходных параметров данных, столько же управляющих параметров и обращается к 10 элементам глобальных данных, с fan-in, равным 3, и fan-out, равным 4,

C \= 1 − 1 5 + 2 × 5 + 5 + 2 × 5 + 10 + 0 + 3 + 4 \= 0.98 {\\displaystyle C=1-{\\frac {1}{5+2\\times 5+5+2\\times 5+10+0+3+4}}=0.98}
