Циклическая зависимость в классах java
У меня есть следующие занятия.
Как можно ясно видеть, существует круговая зависимость между классами. если я попытаюсь запустить класс а, то в конечном итоге получу StackOverflowError .
Если создается граф зависимостей, где узлы являются классами, то эту зависимость можно легко идентифицировать (по крайней мере, для графов с небольшим количеством узлов). Тогда почему JVM не идентифицирует это, по крайней мере, во время выполнения? Вместо метания StackOverflowError , JVM может, по крайней мере, дать предупреждение перед началом выполнения.
[Обновление] некоторые языки не могут иметь циклических зависимостей, потому что тогда исходный код не будет строиться. Например, посмотрите этот вопрос и принятый ответ. Если циклическая зависимость — это запах дизайна для C#, то почему она не для Java? Только потому, что Java может (компилировать код с циклическими зависимостями)?
[update2] недавно найдено jCarder . Согласно веб-сайту, он находит потенциальные тупики, динамически инструментируя Java байтовых кодов и ища циклы в графе объектов. Может ли кто-нибудь объяснить, как инструмент находит циклы?
5 ответов
Форматированный код: http://pastie.org/5074835 У меня есть циклическая зависимость между классом сущностей и классом компонентов. Я попытался перенаправить объявление класса, но мне нужно получить доступ к методу обновления компонента, и таким образом я не могу этого сделать. Есть ли способ.
Я предполагаю, что это обычный вопрос, но я постараюсь описать свою текущую проблему. У меня есть базовый сервис, давайте назовем его ‘CoreService’, который обеспечивает, я бы сказал, функциональность main: обработка данных в DB (у нас есть централизованный DB в наших приложениях). Существует ряд.
Конструктор вашего класса A вызывает конструктор класса B. Конструктор класса B вызывает конструктор класса A. У вас есть бесконечный вызов рекурсии, поэтому в конечном итоге у вас есть StackOverflowError .
Java поддерживает наличие циклических зависимостей между классами, проблема здесь связана только с конструкторами, вызывающими друг друга.
Вы можете попробовать что-то вроде:
В Java вполне допустимо иметь круговую связь между 2 классами (хотя можно было бы задать вопросы о дизайне), однако в вашем случае у вас есть необычное действие каждого экземпляра, создающего экземпляр другого в своем конструкторе (это является фактической причиной StackOverflowError).
Этот конкретный шаблон известен как взаимная рекурсия, в которой у вас есть 2 метода A и B (конструктор в основном является частным случаем метода), а A вызывает B и B вызывает A. Обнаружение бесконечного цикла в отношениях между этими 2 методами возможно в тривиальном случае (том, который вы предоставили), но решение его для общего сродни решению проблемы остановки. Учитывая, что решение проблемы остановки невозможно, комплаенс, как правило, не утруждает себя попытками даже в простых случаях.
Возможно, можно было бы охватить несколько простых случаев, используя шаблон FindBugs, но это было бы неверно для всех случаев.
Это не обязательно так просто, как в вашем примере. Я считаю, что решение этой проблемы было бы равносильно решению проблемы остановки , которая, как мы все знаем, невозможна.
Если у вас действительно есть такой вариант использования, вы можете создавать объекты по требованию (лениво) и использовать getter:
(и аналогично для класса A )., поэтому создаются только необходимые объекты, если, например, вы делаете:
Аналогичный обходной путь для геттеров/сеттеров, использующих композицию и инъекцию конструктора для зависимостей. Важно отметить, что объекты не создают экземпляр для других классов, они передаются (так называемая инъекция).
Я знаю, что этот вопрос не получает достаточного внимания, потому что он встречается не часто, но я хочу прояснить его. Скажем, у меня есть 3 файла: A.h #ifndef A_h #define A_h #include B.h class A < A(); virtual
A(); bool someFunc(B& b); >; #endif B.h #ifndef B_h #define B_h #include A.h.
Хотя практика, которой я следую, может быть неуместной. Все еще ищу решение своей проблемы здесь : Я получаю StackOverflowError :: java.lang.StackOverflowError at com.my.package.pages.Selendroid.HomePage. (HomePage.java:11) at.
Похожие вопросы:
У меня есть следующая циклическая зависимость: $http / \ / \ / \ / \ LoginManager——Interceptor (service) (factory) Эта циклическая зависимость появилась только после того, как я добавил код для.
Возможна ли циклическая зависимость между модулями многомодульного проекта? Вот пример: lazy val client = (project in file(client)) .dependsOn(service) .settings(. ) lazy val service = (project in.
Я пытаюсь разработать схему базы данных для следующих объектов: магазины, продукты, скидки и покупатели. Цель состоит в том, чтобы обеспечить дисконтную кампанию для нескольких продуктов. Однако на.
Форматированный код: http://pastie.org/5074835 У меня есть циклическая зависимость между классом сущностей и классом компонентов. Я попытался перенаправить объявление класса, но мне нужно получить.
Я предполагаю, что это обычный вопрос, но я постараюсь описать свою текущую проблему. У меня есть базовый сервис, давайте назовем его ‘CoreService’, который обеспечивает, я бы сказал.
Я знаю, что этот вопрос не получает достаточного внимания, потому что он встречается не часто, но я хочу прояснить его. Скажем, у меня есть 3 файла: A.h #ifndef A_h #define A_h #include B.h class A.
Хотя практика, которой я следую, может быть неуместной. Все еще ищу решение своей проблемы здесь : Я получаю StackOverflowError :: java.lang.StackOverflowError at.
у меня кончаются идеи. AngularJs утверждает, что в моем проекте существует циклическая зависимость. Мне нужно, чтобы кто-то посмотрел на принципала 4 глаз. Все сводится к следующему code. whenever.
Впервые в жизни я столкнулся с этой проблемой и изо всех сил пытаюсь понять, почему и как ее исправить. У меня есть две службы, service1 и service 2, но, по-видимому, существует такая циклическая.
Я видел следующий фрагмент кода PHP, объявляющий некоторые интерфейсы, абстрактные классы и конкретные классы: public interface MyInterface < public function method1() : MyAbstractClass; >abstract.
Источник
Зависимость циклического компонента Spring Security
В настоящее время я работаю над весенним приложением Vaadin. Согласно спецификациям приложения, аутентификация / авторизация пользователей должны выполняться путем запроса базы данных через jdbcTemplate . Как решить эту проблему? Я использую Spring Boot 1.4.2.RELEASE.
ОБНОВЛЕНИЕ : этот подход работает с Spring Boot 1.1.x.RELEASE, однако в последних версиях он выдает следующее сообщение об ошибке.
Исходный код выглядит так:
Jdbc UserDetailsService s:
P.S Если понизить версию Spring Boot до [1.1.5,1.2.0), эта проблема не возникнет (из-за другой зависимости я должен использовать последнюю версию)
5 ответов
Циклические зависимости
Если вы используете преимущественно внедрение конструктора, можно создать неразрешимый сценарий циклической зависимости.
Например: для класса A требуется экземпляр класса B посредством внедрения конструктора, а для класса B требуется экземпляр класса A посредством внедрения конструктора. Если вы сконфигурируете bean-компоненты для классов A и B для внедрения друг в друга, контейнер Spring IoC обнаруживает эту циклическую ссылку во время выполнения и генерирует BeanCurrentlyInCreationException .
Одно из возможных решений — отредактировать исходный код некоторых классов для настройки сеттерами, а не конструкторами. В качестве альтернативы избегайте внедрения конструктора и используйте только внедрение установщика. Другими словами, хотя это не рекомендуется, вы можете настроить циклические зависимости с помощью установки установки.
В отличие от типичного случая (без циклических зависимостей), циклическая зависимость между bean-компонентом A и bean-компонентом B заставляет один из bean-компонентов вводиться в другой до того, как он сам полностью инициализируется (классический сценарий курица / яйцо).
Я предпочитаю метод @Lazy. Так я смогу придерживаться одного шаблона.
Ваше определение bean-компонента PasswordEncoder находится в WebSecurityConfig , которому требуется JdbcUserDetailsServices . JdbcUserDetailsServices снова зависит от JdbcAccountRepository , которому требуется PasswordEncoder . Так образуется цикл. Простое решение — удалить определение bean-компонента PasswordEncoder из WebSecurityConfig . Даже внутри класса SecurityConfiguration можно решить циклическую задачу.
Одно из решений — не использовать конструктор. Например, вместо:
Вы можете использовать:
Сегодня я тоже столкнулся с такой же проблемой. Я использовал @Lazy в конструкторе одного класса и решил мою проблему:
Источник
Как организовать пакеты (и предотвратить циклы зависимости)?
Я использовал некоторые метрики в своем Java-проекте, и, по-видимому, между пакетами существует много циклов зависимости. Я действительно не знал, как организовать вещи в пакеты, поэтому я просто делал то, что имело для меня смысл, что, по-видимому, неправильно.
Мой проект — это фреймворк нейронной сети. В нейронных сетях есть нейроны, которые связаны друг с другом посредством Connections. Им нужно зависеть друг от друга. Однако существуют также разные типы нейронов, поэтому я подумал, что было бы неплохо поместить их все в собственный пакет «нейронов». Очевидно, что Connection — это не нейрон, поэтому его не должно быть в пакете, но поскольку они ссылаются друг на друга, теперь у меня есть циклическая зависимость.
Это всего лишь пример, но таких ситуаций у меня больше. Как вы справляетесь с подобными ситуациями?
Кроме того, я читал, что классы в пакете выше в иерархии пакетов не должны ссылаться на классы в более глубоких пакетах. Это означало бы, что класс NeuralNetwork в пакете «nn» не может ссылаться на Neuron в пакете «nn.neurons». Вы, ребята, следуете этому принципу? А что, если бы я переместил NeuralNetwork в nn.networks или что-то в этом роде? В этом случае он будет относиться к родительскому пакету, а не к дочернему. Это лучшая практика?
5 ответов
Задача antcontrib VerifyDesign поможет вам сделать то, что вы хотите:
Например, если в одном дереве исходников три пакета
и, естественно, представление должно зависеть только от бизнес-пакета, а бизнес должен зависеть от доступа к данным. Если вы определите свой дизайн таким образом, и он будет нарушен, сборка завершится ошибкой при вызове задачи муравья verifydesign. Например, если я создал класс в biz.xsoftware.presentation и этот класс зависел от класса в biz.xsoftware.dataaccess, сборка завершилась бы ошибкой. Это гарантирует, что дизайн действительно следует тому, что задокументировано (по крайней мере, до некоторой степени). Это особенно хорошо для автоматизированных сборок.
Итак, как только вы решили, как все должно быть организовано, вы можете обеспечить соблюдение требований во время компиляции. Вы также получаете детальный контроль, так что вы можете позволить определенным случаям нарушать эти «правила». Так что вы можете позволить несколько циклов.
В зависимости от того, как вы хотите что-то делать, вы можете обнаружить, что пакет «utils» имеет смысл.
Для конкретного случая, который вы цитируете . я мог бы сделать что-то вроде этого:
- пакет nn содержит Nueron и Connection
- пакет nn.neurons содержит подклассы Nueron
Neuron и Connection — это концепции высокого уровня, используемые в NeuralNetowrk, поэтому их объединение имеет смысл. Классы Neuron и Connection могут ссылаться друг на друга, в то время как классу Connection не нужно знать о подклассах Neuron.
Я не думаю, что циклические зависимости, подобные описанным вами, имеют плохие. Пока взаимозависимые концепции находятся на одном уровне абстракции и относятся к одним и тем же частям архитектуры, нет необходимости скрывать их друг от друга. В моем понимании, нейроны и связи подходят под этот счет.
Обычно для уменьшения таких связей выделяют интерфейсы и, возможно, даже помещают их в отдельный модуль. Простая организация пакетов внутри одного проекта не позволяет в достаточной степени скрыть детали реализации. Распространенный паттерн, позволяющий действительно скрыть реализации, выглядит следующим образом:
Код клиента —-> Интерфейсы 5
Прежде всего, вы справедливо обеспокоены тем, что циклические зависимости между пакетами — это плохо. Проблемы, которые возникают в результате этого, становятся все важнее с увеличением размера проекта, но нет причин вовремя решать эту ситуацию.
Вам следует организовать свои классы, разместив классы, которые вы повторно используете вместе, в одном пакете. Итак, если у вас есть, например, AbstractNeuron и AbstractConnection, вы должны поместить их в один пакет. Если у вас есть реализации HumanNeuron и HumanConnection, вы поместите их в один пакет (например, с именем * .network.human). Или у вас может быть только один тип подключения, например, BaseConnection и много разных нейронов. Принцип остается прежним. Вы размещаете BaseConnection вместе с BaseNeuron. HumanNeuron в собственном пакете вместе с HumanSignal и т. Д. VirtualNeuron вместе с VirtualSignal и т. Д. Вы говорите: «Очевидно, что соединение — это не нейрон, поэтому его не должно быть в пакете…». Это не так очевидно и, если быть точным, правильно.
Вы говорите, что поместили все нейроны в один пакет. Это тоже неверно, если вы повторно не используете все свои реализации вместе. Снова взгляните на схему, которую я описал выше. Либо ваш проект настолько мал, что вы помещаете все в один пакет, либо вы начинаете организовывать пакеты, как описано. Для получения дополнительных сведений см. Общий принцип повторного использования:
КЛАССЫ В ПАКЕТЕ ПОВТОРНО ИСПОЛЬЗУЮТСЯ ВМЕСТЕ. ЕСЛИ ВЫ ПОВТОРНО ИСПОЛЬЗУЕТЕ ОДИН ИЗ КЛАССОВ В ПАКЕТЕ, ВЫ ИСПОЛЬЗУЕТЕ ВСЕ ИХ.
Как вы справляетесь с подобными ситуациями?
Циклические зависимости не являются плохими по своей сути. Фактически, это иногда может быть случаем, когда «лекарство хуже болезни»: извлечение интерфейса увеличивает уровень сложности вашего кода и добавляет еще один уровень косвенности. Скорее всего, этого не стоит для очень простых отношений.
О каком размере кода идет речь? Если у вас всего 10-20 классов, вам, вероятно, не нужно (и не следует) излишне организовывать свой код в пакеты только ради этого.
По мере роста вашего проекта первое различие, которое вы хотите сделать, — это отделить код пользовательского интерфейса от базовой модели данных и логики. Четко разделенные слои имеют решающее значение для правильного модульного тестирования.
Если у вас возникли проблемы с избавлением от циклических зависимостей, вероятно, классы действительно взаимозависимы и должны находиться в одном пакете.
Правильное построение уровней абстракции, вероятно, является одним из наиболее важных аспектов при разработке общей структуры кода.
Источник