Какую стратегию вы используете для именования пакетов в проектах Java и почему? [закрыто]

88

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

В качестве вступления я вижу две основные стратегии именования пакетов. (Для ясности, я не имею в виду всю часть этого «domain.company.project», я говорю о соглашении о пакете под ним.) В любом случае, соглашения об именах пакетов, которые я вижу, следующие:

  1. Функциональность: именование пакетов в соответствии с их архитектурной функцией, а не их идентичностью в соответствии с бизнес-областью. Другой термин для этого может быть именование в соответствии с «слоем». Итак, у вас будет пакет * .ui, пакет * .domain и пакет * .orm. Ваши пакеты представляют собой горизонтальные срезы, а не вертикальные.

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

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

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

    1. Я предпочитаю подходить к системам вертикально, а не горизонтально. Я хочу заняться разработкой системы обработки заказов, а не уровня доступа к данным. Очевидно, есть большая вероятность, что я коснусь уровня доступа к данным при разработке этой системы, но дело в том, что я не думаю об этом так. Это, конечно, означает, что когда я получаю приказ на изменение или хочу реализовать какую-то новую функцию, было бы неплохо не копаться в кучке пакетов, чтобы найти все связанные классы. Вместо этого я просто смотрю в пакет X, потому что то, что я делаю, связано с X.

    2. С точки зрения разработки, я считаю большой победой то, что ваши пакеты документируют вашу бизнес-область, а не вашу архитектуру. Мне кажется, что домен - это почти всегда та часть системы, которую труднее разобрать, поскольку архитектура системы, особенно на данном этапе, становится почти приземленной в ее реализации. Тот факт, что я могу прийти к системе с этим типом соглашения об именах и сразу по именованию пакетов знать, что она имеет дело с заказами, клиентами, предприятиями, продуктами и т. Д., Кажется чертовски удобным.

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

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

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

Тим Вишер
источник
То, что я слышал до сих пор, похоже, указывает на то, что это своего рода призыв к суждению. Однако большинство ответов не касаются практических соображений. Это то, что я ищу. Тем не менее, спасибо за помощь!
Тим Вишер,
Я не думаю, что это приговор. Сначала разделение по слоям, а затем по функциональности - это определенно лучший способ. Я обновляю свой ответ.
eljenso
@eljenso: расскажите об этом ребятам из Eclipse :) В eclipse первое деление - это «функции», которые являются единицами развертывания, а также функциональностью. Внутри «функции» есть «плагины», которые обычно делятся по уровням, но плагины внутри одной «функции» всегда должны развертываться вместе. Если у вас есть только одна единица развертывания, то разделение сначала по уровням, а затем по функциональности может иметь смысл. С несколькими модулями развертывания вы не хотите развертывать только уровень представления - вам нужны дополнительные функциональные возможности.
Peter Štibraný
У меня есть конкретный вопрос относительно бизнес-уровня приложения. Каким должно быть соглашение об именах?
Bionix1441

Ответы:

32

Что касается дизайна упаковки, я сначала делю по слоям, а затем по другим функциям.

Есть еще несколько дополнительных правил:

  1. слои располагаются от самого общего (внизу) к самому частному (вверху)
  2. у каждого уровня есть публичный интерфейс (абстракция)
  3. уровень может зависеть только от публичного интерфейса другого уровня (инкапсуляция)
  4. слой может зависеть только от более общих слоев (зависимости сверху вниз)
  5. слой предпочтительно зависит от слоя непосредственно под ним

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

  • уровень представления: генерирует пользовательский интерфейс, который будет отображаться на уровне клиента.
  • уровень приложения: содержит логику, специфичную для приложения, с отслеживанием состояния
  • уровень обслуживания: группирует функциональность по доменам, без сохранения состояния
  • уровень интеграции: обеспечивает доступ к внутреннему уровню (db, jms, электронная почта, ...)

Для итогового макета пакета действуют некоторые дополнительные правила:

  • корень каждого имени пакета <prefix.company>.<appname>.<layer>
  • Интерфейс уровня дополнительно разделен по функциональности: <root>.<logic>
  • приватная реализация уровня имеет префикс private: <root>.private

Вот пример макета.

Уровень представления разделен по технологии просмотра и, необязательно, по (группам) приложений.

com.company.appname.presentation.internal
com.company.appname.presentation.springmvc.product
com.company.appname.presentation.servlet
...

Уровень приложения разделен на варианты использования.

com.company.appname.application.lookupproduct
com.company.appname.application.internal.lookupproduct
com.company.appname.application.editclient
com.company.appname.application.internal.editclient
...

Уровень обслуживания разделен на бизнес-домены, на которые влияет логика домена на бэкэнд-уровне.

com.company.appname.service.clientservice
com.company.appname.service.internal.jmsclientservice
com.company.appname.service.internal.xmlclientservice
com.company.appname.service.productservice
...

Уровень интеграции разделен на «технологии» и объекты доступа.

com.company.appname.integration.jmsgateway
com.company.appname.integration.internal.mqjmsgateway
com.company.appname.integration.productdao
com.company.appname.integration.internal.dbproductdao
com.company.appname.integration.internal.mockproductdao
...

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

Почему я считаю, что вертикальный подход не так хорош?

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

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

Эльдженсо
источник
«Преимущества такого разделения пакетов заключаются в том, что легче управлять сложностью, а также повышается тестируемость и возможность повторного использования». Вы действительно не вникаете в причины, почему это так.
mangoDrunk
1
Вы не вникаете в причины, почему это не так. Но в любом случае ... правила организации довольно ясны и просты, поэтому это снижает сложность. Это более тестируемое и многоразовое использование благодаря самой организации. Каждый может попробовать, а потом мы можем согласиться или не согласиться. Я кратко объясню некоторые недостатки вертикального подхода, неявно объясняя причины, по которым я считаю, что горизонтальный подход проще.
eljenso
8
В этой статье довольно хорошо объясняется, почему лучше использовать поэтапную функцию, а не послойную .
Tom
@eljenso "Вы не вникаете в причины, почему это не так", пытаясь переложить бремя доказывания? Я думаю, что статья, опубликованная Томом, очень хорошо объясняет, почему пакет за функцией лучше, и мне трудно принять: «Я не думаю, что это суждение. Сначала разделение по слоям, а затем по функциональности - определенно способ ехать "серьезно.
pka
18

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

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

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

Буу Нгуен
источник
Спасибо, это очень ясно. Как я уже сказал в вопросе, я чувствую, что количество раз, когда вы меняете технологии, гораздо меньше, чем вы перемещаете вертикальные срезы функциональности. Разве это не было вашим опытом?
Тим Вишер,
Дело не только в технологиях. Пункт № 1 вашего исходного сообщения имеет смысл только в том случае, если вертикальные срезы представляют собой независимые приложения / службы, взаимодействующие друг с другом через некоторый интерфейс (SOA, если хотите). подробнее ниже ...
Буу Нгуен
Теперь, когда вы перейдете к деталям каждого из этих детализированных приложений / сервисов, которые имеют свой собственный графический интерфейс / бизнес / данные, я с трудом могу представить, что изменения в вертикальном срезе, будь то технология, зависимость, правила / рабочий процесс, ведение журнала , безопасность, стиль пользовательского интерфейса, можно полностью изолировать от других частей.
Буу Нгуен,
Мне были бы интересны ваши отзывы о моем ответе
i3ensays
@BuuNguyen Ссылка на принципы разработки упаковки не работает.
johnnieb
5

Обычно присутствуют оба уровня разделения. Сверху есть подразделения развертывания. Они названы «логически» (в ваших терминах подумайте о функциях Eclipse). Внутри модуля развертывания у вас есть функциональное разделение пакетов (подумайте о плагинах Eclipse).

Например, функция com.feature, и она состоит из com.feature.client, com.feature.coreи com.feature.uiплагинов. Внутри плагинов у меня очень мало разделения на другие пакеты, хотя это тоже не редкость.

Обновление: Кстати, Юрген Хеллер много говорил об организации кода в InfoQ: http://www.infoq.com/presentations/code-organization-large-projects . Юрген - один из архитекторов Spring, и знает толк в этом.

Петер Штибрани
источник
Я не совсем понимаю. Обычно вы можете увидеть com.apache.wicket.x, где x либо функционально, либо логично. Обычно я не вижу com.x. Я полагаю, вы говорите, что выбрали бы структуру com.company.project.feature.layer? У тебя есть причины?
Тим Вишер,
Причина в том, что «com.company.project.feature» - это единица развертывания. На этом уровне некоторые функции являются необязательными и могут быть пропущены (т. Е. Не развернуты). Тем не менее, внутренняя функция не является обязательной, и вы обычно хотите их все. Здесь имеет смысл делить по слоям.
Peter Štibraný
4

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

Обычно части достаточно велики, чтобы разбить их на отдельные артефакты сборки, где вы можете поместить основные функции в одну банку, API в другую, материалы веб-интерфейса в файл war и т. Д.

Бен Харди
источник
Как это может выглядеть? domain.company.project.function.logicalSlice?
Тим Вишер,
довольно много! egdcpweb.profiles, dcpweb.registration, dcpapis, dcppersistence и т. д. в целом работает довольно хорошо, ваш опыт может отличаться. зависит также от того, используете ли вы моделирование домена - если у вас несколько доменов, вы можете сначала разделить их по доменам.
Бен Харди,
Лично я предпочитаю обратное, логичное, а не функциональное. apples.rpc, apples.model, а затем banana.model, banana.store
.
Более ценно иметь возможность сгруппировать все яблоки вместе, чем все веб-материалы вместе.
М.П.
3

Пакеты должны составляться и распространяться как единое целое. При рассмотрении того, какие классы принадлежат пакету, одним из ключевых критериев является его зависимости. От каких других пакетов (включая сторонние библиотеки) зависит этот класс. Хорошо организованная система объединяет классы с похожими зависимостями в пакет. Это ограничивает влияние изменения в одной библиотеке, поскольку от него будут зависеть только несколько четко определенных пакетов.

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

Эриксон
источник
Ах. Таким образом, горизонтальные срезы защищают вас от фактических обновлений в библиотеках, которые вы используете. Я думаю, вы правы в том, что вертикальные срезы размывают все зависимости вашей системы от каждого пакета. Почему это так важно?
Тим Вишер,
Для «фичи» это не такая уж и большая проблема. Они, как правило, более изменчивы и в любом случае имеют больше зависимостей. С другой стороны, этот «клиентский» код имеет тенденцию к низкой возможности повторного использования. Для чего-то, что вы собираетесь использовать в качестве многоразовой библиотеки, изоляция клиентов от каждого небольшого изменения - отличное вложение.
erickson
3

Лично я предпочитаю логически группировать классы, а не включать в них подпакеты для каждого функционального участия.

Цели упаковки

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

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

Пример моей логической структуры упаковки

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

apple.model apple.store banana.model banana.store

Преимущества

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

Прочие логические и функциональные преимущества

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

Почему функциональность?

Какой ценности можно достичь, объединяя вещи по функциональности. Большинство классов в таком случае независимы друг от друга, и необходимость в использовании частных методов или классов пакета практически отсутствует. Реорганизация их в собственные пакеты мало что дает, но помогает уменьшить беспорядок.

Разработчик вносит изменения в систему

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

mP.
источник
«Когда разработчики [...] файлы из всех областей дерева пакетов». Вы правы, когда говорите, что это кажется глупым. Потому что это как раз и есть смысл правильного разделения на уровни: изменения не распространяются на всю структуру приложения.
eljenso
Спасибо за совет. Я не уверен, что правильно тебя понимаю. Я считаю, что вы пытаетесь отстаивать логические пакеты, я не совсем понимаю, почему. Не могли бы вы сделать свой ответ немного яснее, возможно, перефразируя? Благодарность!
Тим Вишер,
3

И функциональный (архитектурный), и логический (функциональный) подходы к упаковке имеют место. Многие примеры приложений (которые можно найти в учебниках и т. Д.) Следуют функциональному подходу, заключающемуся в размещении презентаций, бизнес-сервисов, сопоставления данных и других архитектурных уровней в отдельных пакетах. В примерах приложений каждый пакет часто имеет только несколько или только один класс.

Этот первоначальный подход хорош, поскольку надуманный пример часто служит для: 1) концептуального отображения архитектуры представляемой структуры, 2) выполняется с единственной логической целью (например, добавление / удаление / обновление / удаление домашних животных из клиники) . Проблема в том, что многие читатели принимают это за стандарт, не имеющий границ.

По мере того, как «бизнес-приложение» расширяется и включает все больше и больше функций, следование функциональному подходу становится обузой. Хотя я знаю, где искать типы, основанные на уровне архитектуры (например, веб-контроллеры в пакете «web» или «ui» и т. Д.), Разработка единой логической функции начинает требовать перехода между множеством пакетов. Это, по крайней мере, громоздко, но еще хуже.

Поскольку логически связанные типы не упаковываются вместе, API чрезмерно разглашается; взаимодействие между логически связанными типами должно быть «общедоступным», чтобы типы могли импортировать и взаимодействовать друг с другом (теряется возможность минимизировать видимость по умолчанию / пакет).

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

И наоборот, при создании бизнес-приложения я буду упаковывать его по функциям. У меня нет проблем с размещением Widget, WidgetService и WidgetController в одном пакете com.myorg.widget. С последующим использованием преимущества видимости по умолчанию (с меньшим количеством операторов импорта, а также зависимостей между пакетами).

Однако есть перекрестные случаи. Если мой WidgetService используется многими логическими доменами (функциями), я могу создать пакет com.myorg.common.service . Также велика вероятность того, что я создаю классы с намерением повторно использовать их в различных функциях и в итоге получу такие пакеты, как com.myorg.common.ui.helpers. И com.myorg.common.util .. Я могу даже переместить все эти более поздние «общие» классы в отдельный проект и включить их в свое бизнес-приложение как зависимость myorg-commons.jar.

ревизии i3ensays
источник
2

Это зависит от степени детализации ваших логических процессов?

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

Проект, над которым я сейчас работаю, ошибается в сторону логического разделения, есть пакет для аспекта jython, пакет для механизма правил, пакеты для foo, bar, binglewozzle и т. Д. Я ищу специальные синтаксические анализаторы XML / Writers для каждого модуля в этом пакете, вместо того, чтобы иметь пакет XML (что я делал ранее), хотя по-прежнему будет базовый пакет XML, в котором идет общая логика. Однако одна из причин этого заключается в том, что он может быть расширяемым (плагины), и, следовательно, каждый плагин также должен будет определять свой код XML (или базы данных и т. Д.), Поэтому централизация этого может вызвать проблемы позже.

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

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

Или, может, я болтаю.

JeeBee
источник
Я не совсем понимаю вашу точку зрения. Я думаю, что вы говорите, что вы должны просто делать то, что имеет наибольший смысл для вашего проекта, и для вас это логично с добавлением небольшого количества функциональности. Есть ли для этого конкретные практические причины?
Тим Вишер,
Как я уже сказал, у меня будут плагины для расширения встроенной функциональности. Плагин должен уметь анализировать и записывать свой XML (и, в конечном итоге, в БД), а также обеспечивать функциональность. Следовательно, у меня есть com.xx.feature.xml или com.xx.xml.feature? Первое кажется более аккуратным.
JeeBee
2

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

Для меня это намного проще поддерживать и визуализировать в вертикальной системе именования, а не в горизонтальной. если component1.display имеет ссылку на component2.dataaccess, это вызывает больше предупреждений, чем если бы display.component1 имел ссылку на dataaccess. компонент2.

Конечно, компоненты, общие для обоих, идут в собственном пакете.

леванд
источник
Насколько я понимаю, вы выступаете за соглашение о вертикальном именовании. Я думаю, что для этого нужен пакет * .utils, когда вам нужен класс в нескольких срезах.
Тим Вишер,
2

Я полностью слежу и предлагаю логичную («побочную») организацию! Пакет должен как можно точнее следовать концепции «модуля». Функциональная организация может распространять модуль по проекту, что приводит к меньшей инкапсуляции и подверженности изменениям в деталях реализации.

Возьмем, к примеру, плагин Eclipse: размещение всех представлений или действий в одном пакете было бы беспорядком. Вместо этого каждый компонент функции должен перейти в пакет функции или, если их много, в подпакеты (обработчики featureA., настройки featureA. и т. Д.)

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

thSoft
источник
1

Я лично выбрал бы функциональное именование. Краткая причина: это позволяет избежать дублирования кода или кошмара зависимостей.

Позвольте мне немного уточнить. Что происходит, когда вы используете внешний файл jar с собственным деревом пакетов? Вы эффективно импортируете (скомпилированный) код в свой проект, а вместе с ним (функционально разделенное) дерево пакетов. Имеет ли смысл использовать два соглашения об именах одновременно? Нет, если это не было скрыто от вас. И это так, если ваш проект достаточно мал и состоит из одного компонента. Но если у вас несколько логических модулей, вы, вероятно, не захотите повторно реализовывать, скажем, модуль загрузки файла данных. Вы хотите разделить его между логическими модулями, не иметь искусственных зависимостей между логически несвязанными модулями, и вам не нужно выбирать, в какой модуль вы собираетесь поместить этот конкретный общий инструмент.

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

Постараюсь точнее ответить на каждый из ваших пунктов по логическому именованию.

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

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

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

  4. В больших коммерческих проектах смена технологий происходит чаще, чем вы думаете. Например, мне пришлось уже 3 раза менять XML-парсер, 2 раза технологию кеширования и 2 раза программное обеспечение геолокации. Хорошо, что я спрятал все мелкие детали в специальном пакете ...

Вархан
источник
1
Простите меня, если я ошибаюсь, но мне кажется, что вы больше говорите о разработке фреймворка, предназначенного для использования многими людьми. Я думаю, что правила меняются между этим типом разработки и разработкой систем для конечных пользователей. Я ошибся?
Тим Вишер,
Я думаю, что для общих классов, используемых многими вертикальными срезами, имеет смысл иметь что-то вроде utilsпакета. Тогда любые классы, которым это нужно, могут просто зависеть от этого пакета.
Тим Вишер,
И снова размер имеет значение: когда проект становится достаточно большим, его нужно поддерживать несколькими людьми, и возникает какая-то специализация. Чтобы упростить взаимодействие и предотвратить ошибки, быстро становится необходимым разбиение проекта на части. А пакет utils скоро станет огромным!
Вархан
1

Интересный эксперимент - вообще не использовать пакеты (кроме корневого пакета).

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

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

user53682
источник
0

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

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

Билл Мичелл
источник
Разве многослойные системы не всегда зависят от следующего уровня? Например, пользовательский интерфейс зависит от уровня ваших сервисов, который зависит от уровня вашего домена и т. Д. Объединение вертикальных срезов вместе, кажется, защищает от чрезмерных зависимостей между пакетами, не так ли?
Тим Вишер,
Я почти никогда не использую защищенные методы и методы по умолчанию. 99% являются государственными или частными. Некоторые исключения: (1) видимость по умолчанию для методов, используемых только модульными тестами, (2) защищенный абстрактный метод, который используется только из абстрактного базового класса.
Эско Луонтола,
1
Тим Вишер, зависимости между пакетами не являются проблемой, пока зависимости всегда указывают в одном направлении и на графике зависимостей нет циклов.
Эско Луонтола,
0

Это зависит. В моей работе мы иногда разделяем пакеты по функциям (доступ к данным, аналитика) или по классам активов (кредит, акции, процентные ставки). Просто выберите структуру, наиболее удобную для вашей команды.

Quant_dev
источник
Есть ли причина идти в другую сторону?
Тим Вишер,
Разделение по классам активов (в более общем смысле: по бизнес-доменам) облегчает новым людям поиск в коде. Разделение по функциям хорошо для инкапсуляции. Для меня «пакетный» доступ в Java сродни «другу» в C ++.
Quant_dev
@quant_dev Я не согласен с тем, что "функция ..by хороша для инкапсуляции". Я думаю, вы имеете в виду полную противоположность. Кроме того, не выбирайте только «удобство». Для каждого есть футляр (см. Мой ответ).
i3ensays 04
-3

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

Рагху Кастури
источник
5
Повторное использование кода связано не с ценой на оборудование, а с затратами на обслуживание кода.
user2793390
1
Согласитесь, но исправление чего-либо в простом модуле не должно влиять на весь код. О каких затратах на обслуживание вы говорите?
Рагху Кастури
1
Исправление ошибки в модуле должно повлиять на все приложение. Зачем вам нужно исправлять одну и ту же ошибку несколько раз?
user2793390 08