Круговая зависимость - Circular dependency

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

Обзор

Круговые зависимости естественны для многих модели предметной области где определенные объекты одного домена зависят друг от друга.[1] Однако в разработка программного обеспечения, круговые зависимости между более крупными программными модулями считаются антипаттерн из-за их негативного воздействия.[1] Несмотря на это, было обнаружено, что такие циклические (или циклические) зависимости широко распространены среди исходных файлов реального программного обеспечения.[2] Однако взаимно-рекурсивные модули довольно часто встречаются в функциональное программирование, где часто приветствуются индуктивные и рекурсивные определения.

Проблемы

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

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

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

Причины и решения

В очень больших проектах программного обеспечения инженеры-программисты могут потерять контекст и непреднамеренно ввести циклические зависимости. Существуют инструменты для анализа программного обеспечения и поиска нежелательных циклических зависимостей.[3]

Циклические зависимости могут быть введены при реализации Перезвоните функциональность. Этого можно избежать, применив шаблоны проектирования словно образец наблюдателя.

Пример на C ++

Реализация циклических зависимостей в C / C ++ может быть немного сложной, потому что любая структура или определение класса должны быть помещены выше их использования в том же файле. Циклическая зависимость между классами А и B таким образом, оба потребуют определения А быть помещенным выше B, и определение B быть помещенным выше А, что, конечно, невозможно. А предварительная декларация поэтому необходимо для этого.

В следующем примере показано, как это делается.

  • Файл а:
#ifndef A_H#define A_Hучебный класс B;	// предварительное объявлениеучебный класс А {общественный:	B* б;};#endif // A_H
  • Файл b.h:
#ifndef B_H#define B_H учебный класс А;	// предварительное объявлениеучебный класс B {общественный:	А* а;};#endif // B_H
  • Файл main.cpp:
#включают "ах"#включают "b.h" int главный() {	А а;	B б;	а.б = &б;	б.а = &а;}

Обратите внимание, что хотя имя (например, А) возможно объявлен несколько раз, например, в форвардных объявлениях, это может быть только определенный однажды Одно правило определения ).

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

Ниже приведен еще один пример прямого объявления, который может быть полезен, если приложению требуется самоподдерживающийся массив объектов, который может добавлять и удалять объекты во время выполнения:

  • Файл а:
учебный класс А {общественный:    статический А *первый, *последний;    А *предыдущий, *следующий;    А();    ~А();};

В статические переменные first и last должны быть определены, потому что их объявление не резервирует для них место в памяти. Примечание: статические переменные не меняются от объекта к объекту и остаются неизменными для данного класса.

Они также должны быть инициализированы 0 или NULL, чтобы мы знали, с чего они должны начинать.

  • Файл a.cpp:
#включают "ах"А *А::первый=0, *А::последний=0; // не помещайте здесь слово static, это вызовет ошибкуА::А() {    если (первый == 0) первый=это; // создан первый A    предыдущий = последний;    если (предыдущий != 0) предыдущий->следующий = это;    последний = это;    следующий = 0;}А::~А() {    если (предыдущий != 0) предыдущий->следующий = следующий;    если (следующий != 0) следующий->предыдущий = предыдущий;}

Смотрите также

Рекомендации

  1. ^ а б Лакос, Джон (1996-07-20). Разработка крупномасштабного программного обеспечения на C ++ (1-е изд.). Бостон: Эддисон-Уэсли. ISBN  9780201633627.
  2. ^ Мелтон, Хайден; Темперо, Юэн (2007-01-12). «Эмпирическое исследование циклов среди классов в Java». Эмпирическая разработка программного обеспечения. 12 (4): 389–415. CiteSeerX  10.1.1.141.5362. Дои:10.1007 / s10664-006-9033-1. ISSN  1382-3256.
  3. ^ JDepend за Ява

внешняя ссылка