Плохи ли все циклические ссылки между пакетами Java?

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

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

Думаю, я полностью понимаю, где такая зависимость между пакетами - это плохо. Например, в типичном трехуровневом многоуровневом дизайне представления/службы/постоянства почти всегда плохая идея разрешить коду обработки базы данных иметь ссылку на классы, связанные с пользовательским интерфейсом. У меня нет проблем с тем, чтобы назвать это «нарушением».

Но давайте рассмотрим другие случаи, например, разработку приложения, подобного IDE. Скажем, у нас есть основной пакет, который содержит интерфейс Application, который определяет метод List Application.getViews() для ссылки на представления приложения.

Однако, когда в интерфейсе View есть метод Application getApplication() для возврата к родительскому приложению, что, как мне кажется, является довольно распространенным дизайном, он вводит циклическую ссылку , при условии, что каждый из интерфейсов разделен в com.myapp.ui и com.myapp.ui.view соответственно.

Конечно, вы можете просто поместить интерфейс View в com.myapp.ui, чтобы разорвать цикл. Но если в com.myapp.ui.view есть различные другие APIS, связанные с представлением, многие из них являются другими абстрактными API, такими как AbstractView, ContentView, AbstractContentView и т. д. Интересно, не целесообразнее ли хранить их в отдельных пакетах для целей управления.

И учтите, что указанное приложение имеет много других подобных случаев, таких как com.myapp.ui.action, com.myapp.ui.perspective и т. д., что действительно сделало бы Пакет com.myapp.ui переполнен, если мы собираемся поместить их все туда.

Итак, какой подход вы предлагаете, чтобы справиться с такой ситуацией? Действительно ли каждая циклическая ссылка на пакет — это плохо? Или, если мне придется с ними жить, как настроить Sonar для проверки только реальных проблемных циклов?

Спасибо!


person mysticfall    schedule 14.05.2013    source источник


Ответы (2)


Каждый абсолют - кроме этого ;) - будет иногда ошибаться. Итак, плоха ли каждая циклическая ссылка? Нет. Вы должны использовать свое суждение.

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

Чтобы использовать ваш пример, действительно ли представлению нужен getApplication(), который предположительно возвращает относительно «тяжелый» объект (т.е. тот, который сам нуждается в базе данных, сети и т. д. и т. д.)? Возможно... а может и нет. Если то, что вам действительно нужно от этого getApplication, это что-то с несколькими обратными вызовами (например, когда пользователь инициирует какое-то действие), то может быть полезно создать интерфейс в каком-то общем пакете для этого обратного вызова. Итак, вместо:

com.foo.app.Application
com.foo.view.View
    Application getApplication()

У вас будет:

com.foo.common.Callback // maybe just a Callable, Runnable, etc?
com.foo.app.Application
    provides a Callback for some action foo
com.foo.view.View
    Callback getFooCallback()

Вопрос, который вы должны задать: что это мне дает? Может случиться так, что вам нужно так много заглушить, что это не даст вам многого, хотя это может означать, что вы можете немного разбить свои классы. Но может случиться так, что на самом деле это упростит тестирование вашего представления, потому что теперь ваш модульный тест может (1) тестировать представление, не запуская все приложение, и (б) предоставить «фиктивный» обратный вызов, который делает что-то вроде сохранения строку, описывающую действие, а затем ваш модульный тест утверждает, что он сохранил правильную строку.

person yshavit    schedule 14.05.2013
comment
Позвольте мне попытаться придумать случай, когда View.getApplication() может быть оправдан. Я предполагаю, что может быть общим требованием, чтобы представление ссылалось на другие ресурсы, обычно связанные с приложением, такие как пакет сообщений i18n или источник данных JDBC и т. д. Возможно, мы все можем отсортировать их способом DI, но я считаю, что По определению, представление в этом случае является частью составного целого, которое нельзя мыслить в отдельном контексте. Итак, источник данных JDBC не может быть произвольным, а таким, который предоставляет его родительское приложение. не было бы более ясно показать такие зависимости и в дизайне? - person mysticfall; 14.05.2013

И действительно, есть открытый тикет JIRA, чтобы предотвратить рассмотрение цикла между пакетами отца и ребенка как недостаток качества: http://jira.codehaus.org/browse/SONAR-3452

person Freddy - SonarSource Team    schedule 26.05.2013