Почему циклические ссылки в Visual Studio - плохая практика?

Почему циклические ссылки в Visual Studio - плохая практика?

Во-первых, я опишу пример того, как это может происходить при использовании C# в Visual Studio, поскольку VS обычно сообщает вам, есть ли у вас циклическая ссылка, и предотвращает ее.

Сначала создается класс Утилиты, полагающийся только на код, предоставленный вам Visual Studio и .Net. Затем создается класс Электронная почта, который зависит от Утилиты. Вместо добавления обоих проектов в одно решение создается новое решение и добавляется ссылка на Utilities.dll. Затем, спустя некоторое время, кто-то решает, что он хочет, чтобы класс Утилиты отправлял электронное письмо, и добавляет ссылку на Email.dll. Visual Studio с радостью позволит вам это сделать, но теперь исходный код не будет компилироваться как есть без одного из двоичных файлов.

На моем рабочем месте это стандартная процедура - копировать и вставлять двоичные файлы при разработке, а затем создавать только те проекты, над которыми вы работаете. Это привело как минимум к одной циклической ссылке в кодовой базе, которая оставалась незамеченной более 3 лет.

Мне это кажется очень плохой практикой, потому что нет способа собрать любой проект из исходного кода, не имея предварительно библиотеки DLL. Этот аргумент не подходит «практичным» людям, с которыми я работаю, поскольку маловероятно, что мы потеряем все копии наших двоичных файлов одновременно. Бинарные файлы никогда не хранятся в системе контроля версий, что только беспокоит меня.

Это похоже на ситуацию, которой следует избегать, но не на ситуацию, которая представляет какую-либо заметную угрозу. Действительно ли циркулярные ссылки между проектами имеют большое значение или я преувеличиваю?

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

epochwolf 21.11.2008 17:29
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
31
1
17 031
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

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

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

Если они придут с чем-то отличным от «Мы всегда так поступали», я бы хотел это услышать. И, как всем известно, «мы всегда так поступали» НЕ является хорошей причиной, и они не должны ее защищать.

«мы всегда так поступали» - это один шаг от «Изменение этого стоит денег компании»

Jimmy 25.11.2008 22:58

Компании будет стоить больше денег не

Vinko Vrsalovic 26.11.2008 01:01
Ответ принят как подходящий

Да, это плохая практика именно по той причине, которую вы указали - вы не можете перестроить из исходного кода.

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

Juozas Kontvainis 20.04.2009 10:47

Хм, я думаю, что с точки зрения вопроса OP, о проектах строительство с нуля, это плохая практика из-за:

  1. Невозможность сборки из исходного кода / с нуля - что, если я буду поддерживать несколько версий кода. Между каждой версией было внесено несколько изменений в каждую из библиотек. Единственный способ, которым я могу поддерживать эти параллельные версии, - это теперь хранить скомпилированные двоичные файлы в моем элементе управления версиями. Это также означает, что сравнивать каждую версию библиотеки намного сложнее. (Мне нужно выяснить, какая версия используется, а затем посмотреть фактический исходный код библиотеки, а не просто прямую разницу в самом источнике.
  2. Возможно, сложнее распространять патчи клиентам, у которых есть инкрементно патченные версии. Где и что патчить?

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

Я часто это замечал на сборках утилит. Должен быть антипаттерн.

Круговые зависимости плохи, потому что:

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

это потенциально бесконечный цикл в процессе сборки

Это один из симптомов сверхмодуляризации.

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

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

Блин.

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

Конечно, модульность - это вообще хорошо.

Но, как и все хорошее, его можно довести до смехотворных крайностей.

Мой совет - найти эти циклические зависимости и объединить проекты в более крупные куски, поскольку текущая разбивка проекта, вероятно, представляет собой ложную модульность. Лучше разделить ваш большой проект на несколько хорошо разделенных модулей, чем иметь миллиард псевдомодулей, создающих искусственные границы между логически связанными классами.

Я слышал, что упаковка Nuget для компонентов проекта базового уровня, таких как объекты DAL и POCO, является хорошей идеей, потому что вы можете установить пакеты Nuget в нескольких решениях. Вы слышали то же самое? Если да, не могли бы вы объяснить, как я могу использовать упаковку Nuget в такой ситуации?

CS Lewis 29.10.2015 12:23

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

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

Кроме того, MSBuild позволяет использовать файл SLN для сборки всего, если все ваши проекты используют VB или C#. Это позволяет создать файл uber-решения, который идеально подходит для автоматизированного процесса сборки. Загвоздка в том, что для того, чтобы такой главный файл решения работал, все проекты в SLN должны использовать ссылки на проекты. Следовательно, чтобы воспользоваться этим преимуществом, Microsoft по определению ожидает, что вы не используете циклические ссылки, поскольку они явно запрещены IDE Visual Studio (для ссылок на проекты).

Скотт Хансельман ссылается на файл uber-решения в следующем сообщении:
Взлом: параллельные сборки MSBuild из среды Visual Studio IDE
. http://www.hanselman.com/blog/CategoryView.aspx?category=MSBuild

Если вам трудно убедить своих «прагматичных» коллег в том, что это плохая практика, предложите прагматичное решение: удалите эти циклические ссылки, и время сборки сократится, так как можно избежать множества ненужных перестроек.

Другие вопросы по теме