Как организовать пакеты (и предотвратить циклы зависимости)? - PullRequest
27 голосов
/ 22 марта 2009

Я запустил некоторые метрики в своем Java-проекте, и, очевидно, между пакетами существует много циклов зависимости. Я не знал, как организовать вещи в пакеты, поэтому я сделал то, что имело для меня смысл, что, по-видимому, неправильно.

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

Это всего лишь пример, но у меня больше таких ситуаций. Как вы справляетесь с такими ситуациями?

Кроме того, я прочитал, что классы в пакете выше в иерархии пакетов не должны ссылаться на классы в пакетах, которые глубже. Это будет означать, что класс NeuralNetwork в пакете 'nn' не может ссылаться на Neuron в пакете 'nn.neurons'. Ребята, вы придерживаетесь этого принципа? А что, если бы я переместил NeuralNetwork в nn.networks или что-то в этом роде? В этом случае, это будет относиться к пакету родного брата вместо ребенка. Это лучшая практика?

Ответы [ 5 ]

13 голосов
/ 22 марта 2009

Задача antcontrib VerifyDesign поможет вам сделать то, что вы хотите:

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

* biz.xsoftware.presentation
* biz.xsoftware.business
* biz.xsoftware.dataaccess

и естественно презентация должна только зависит от бизнес-пакета, и бизнес должен зависеть от доступа к данным. Если вы определите свой дизайн таким образом и нарушено, сборка не удастся когда задана задача проверки дизайна называется. Например, если я создал класс в biz.xsoftware.presentation и этот класс зависит от класса в biz.xsoftware.dataaccess, сборка потерпит неудачу Это обеспечивает дизайн фактически следует тому, что задокументировано в некоторой степени, по крайней мере). Это особенно хорошо с автоматизированными сборками

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

В зависимости от того, как вы хотите что-то делать, может оказаться, что пакет «utils» имеет смысл.

Для конкретного случая, который вы цитируете ... Я мог бы сделать что-то вроде этого:

  • пакет nn содержит Nueron и Connection
  • пакет nn.neurons содержит подклассы Nueron

Neuron и Connection - это концепции высокого уровня, используемые в NeuralNetowrk, поэтому объединение их всех имеет смысл. Классы Neuron и Connection могут ссылаться друг на друга, в то время как классу Connection не нужно знать о подклассах Neuron.

9 голосов
/ 22 марта 2009

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

Вы должны организовать свои классы, поместив классы, которые вы повторно используете вместе, в один пакет. Итак, если у вас есть, например, AbstractNeuron и AbstractConnection, вы поместите их в один пакет. Если у вас теперь есть реализации HumanNeuron и HumanConnection, вы бы поместили их в один пакет (называемый, например, * .network.human). Или у вас может быть только один тип соединения, например, BaseConnection и много разных нейронов. Принцип остается прежним. Вы размещаете BaseConnection вместе с BaseNeuron. HumanNeuron в отдельном пакете вместе с HumanSignal и т. Д. VirtualNeuron вместе с VirtualSignal и т. Д. Вы говорите: «Очевидно, что Соединение не является Нейроном, поэтому его не должно быть в пакете ...». Это не так очевидно и не совсем правильно.

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

КЛАССЫ В ПАКЕТЕ ВМЕСТЕ ПЕРЕСМОТРЕНЫ. ЕСЛИ ТЫ ПОВТОРЯЙТЕ ОДИН ИЗ КЛАССОВ В ПАКЕТЕ, ВЫ ИСПОЛЬЗУЕТЕ ИХ ALL.

5 голосов
/ 22 марта 2009

О каком размере кода мы говорим? Если у вас всего 10-20 классов, вам, вероятно, не нужно (и не нужно) чрезмерно организовывать код в пакеты только ради него.

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

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

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

5 голосов
/ 22 марта 2009

Как вы справляетесь с такими ситуациями?

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

5 голосов
/ 22 марта 2009

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

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

Код клиента ----> Интерфейсы <--- Реализация </p>

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

Вложение пакетов служит нескольким целям: Некоторые проекты могут иметь модель домена, которая организована в пакеты. В этом случае пакеты отражают некоторую группировку домена, и ссылки могут идти вверх / вниз пакетов. Когда речь заходит о таких вещах, как реализация сервисов, предлагаемый вами шаблон является довольно распространенным и стоит следовать. Чем глубже вы получаете иерархию пакетов, тем более конкретным считается класс.

...