Следует ли помещать интерфейсы в отдельный пакет? [закрыто]


80

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

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


4
Почему это помечено как [языково-независимый]?
finnw

Другая ситуация, когда это может быть предпочтительнее, если модуль должен передаваться через некоторый rpc, а не прямой вызов - тогда интерфейс пригодится для генерации прокси для клиента ??
Redzedi

Ответы:


101

Размещение и интерфейса, и реализации - обычное дело и не кажется проблемой.

Возьмем, к примеру, Java API - у большинства классов оба интерфейса и их реализации включены в один пакет.

Возьмем, к примеру, java.utilпакет:

Он содержит интерфейсы , такие как Set, Map, List, в то же время имея реализаций , таких как HashSet, HashMapи ArrayList.

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

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

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

  • Добавьте к имени интерфейса префикс I. Этот подход используется с интерфейсами в .NET framework. Было бы довольно легко сказать, что IListэто интерфейс для списка.

  • Используйте ableсуффикс - . Такой подход часто рассматривается в API Java, например Comparable, Iterableи Serializableнекоторые из них.


43
+1 хотя я бы не рекомендовал соглашение об именах I *, если вы не находитесь в .NETverse, и даже тогда только для единообразия
CurtainDog

7
Я использовал это соглашение в java-коде, и я нашел его полезным .... YMMV
hhafez

5
Я знаю, что это немного устарело, но я обнаружил, что сейчас предлагается четко назвать интерфейс и назвать реализацию чем-то более странным. например, Interface = Store Implementation = StoreImpl В основном, чтобы подчеркнуть, что интерфейс должен использоваться поверх реализации.
Frostymarvelous

1
Я считаю, что такой подход, даже если он кажется правильным, приводит к архитектурным проблемам. Например, нет способа определить библиотеки, которые используют только интерфейсы, а затем реализовать их в общей библиотеке, то есть на уровне инфраструктуры.
Лука Мазера

1
Я согласен с комментарием об отказе от использования префикса «I» для интерфейсов точно так же, как я бы не рекомендовал использовать суффикс «impl» для реализаций. Это хорошая статья, отражающая эти случаи: octoperf.com/blog/2016 / 10/27 / impl-classes-are-evil
raspacorp

19

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

Давайте посмотрим на этот конкретный экземпляр.

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

Таким образом, это пахнет правилом без веской причины: оно принимает решение на основании чего-то, что является общедоступным, но это решение не оказывает никакого влияния на то, что является общедоступным.

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


Учитывая тег «Java» на вопрос - что такое смысл « публично подвергается пакет», в Java? Или ваш ответ не зависит от Java? Я думал только пакет пользователей (например, классы, интерфейсы и их членов), сами не упаковывает, был контроль доступа спецификаторов , как «общественный», «частной» и т.д. (это обсуждается далее @ вопрос stackoverflow.com/questions/4388715/... )
bacar

Я только что уточнил формулировку; Под «общедоступным пакетом» я действительно имел в виду «пакет с общедоступными вещами».
cjs 06

Конечно, во всех пакетах должны быть публичные вещи; если нет, как вы можете использовать пакет?
bacar

В данном конкретном случае были пакеты, в которых не было ничего общедоступного, но которые использовались пакетами, делающими общедоступными. Таким образом, первое нельзя использовать напрямую, а только косвенно через второе.
cjs 07

1
Но применим тот же вопрос. Как могут пакеты, которые сделали вещи общедоступными, использовать пакеты, которые ничего не сделали общедоступными? Я не знаю способа в Java, чтобы заставить «первое нельзя использовать напрямую, а только косвенно через второе». Если в вашем пакете реализации есть только частные члены, он так же недоступен для вашего интерфейсного пакета, как и для клиента. & аналогично для других областей доступа. В Java нет эквивалента friend(C ++) или internal(C #).
bacar

10

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


7

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


7
У вас может быть отдельный проект API (который является отдельным распространяемым), и это по-прежнему не мешает интерфейсам и реализации находиться в одном пакете. Это немного похоже на то, что у вас есть модульные тесты в том же пакете, что и тестируемые классы, но все же в отдельном артефакте сборки. Банки и пакеты в Java в значительной степени ортогональны.
Джордж

7

В книге « Практическая разработка программного обеспечения: подход к изучению конкретного случая» предлагается размещать интерфейсы в отдельных проектах / пакетах.

Архитектура PCMEF +, о которой говорится в книге, имеет следующие принципы:

  1. Принцип нисходящей зависимости (DDP)
  2. Принцип восходящего уведомления (UNP)
  3. Принцип связи с соседями (NCP)
  4. Принцип явной ассоциации (EAP)
  5. Принцип исключения цикла (CEP)
  6. Принцип именования классов (CNP)
  7. Принцип пакета знакомств (APP)

Описание принципов №3 и №7 объясняет, почему это хорошая идея:

Принцип связи между соседями требует, чтобы пакет мог напрямую связываться только со своим соседним пакетом. Этот принцип гарантирует, что система не распадется на несжимаемую сеть взаимосвязанных объектов. Чтобы обеспечить соблюдение этого принципа, при передаче сообщений между несоседними объектами используется делегирование (раздел 9.1.5.1). В более сложных сценариях пакет ознакомления (раздел 9.1.8.2) можно использовать для группировки интерфейсов, чтобы помочь в совместной работе с удаленными пакетами.

Принцип пакета знакомств является следствием принципа связи с соседями. Пакет ознакомления состоит из интерфейсов, которые объект передает вместо конкретных объектов в аргументах для вызовов методов. Интерфейсы могут быть реализованы в любом пакете PCMEF. Это позволяет эффективно взаимодействовать между несоседними пакетами при централизации управления зависимостями для одного пакета ознакомления. Необходимость ознакомительного пакета была объяснена в Разделе 9.1.8.2 и снова обсуждается далее в контексте PCMEF.

См. Эту ссылку: http://comp.mq.edu.au/books/pse/about_book/Ch9.pdf


5

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


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

2
В любом случае, это не так уж и сложно ...
Пол Сонье

4
-1 Да, это так. Это излишне затрудняет поиск реализаций и разделяет вещи, которые принадлежат друг другу.
Майкл Боргвардт

5
И не публиковать реализации - это совершенно другое дело, которое можно сделать, сделав их закрытыми для пакета, и НЕ МОЖЕТ быть сделано, поместив их в отдельный пакет. Не существует такого понятия, как закрытый пакет.
Майкл Боргвардт

1
Более того, похоже, что все интерфейсы будут излишне общедоступными.
Adeel Ansari

4

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


7
Как это помогает вам переключать навесное оборудование?
Януш

см. ответ Кэмерона Поупа, это именно та причина, по которой мы делаем это здесь
хафез,

3

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


2

Обычно я добавляю интерфейсы к реализации, однако я могу понять, почему вы можете захотеть сохранить их отдельно. Скажем, например, кто-то хотел переопределить классы на основе ваших интерфейсов, им понадобится jar / lib / etc с вашей реализацией, а не только интерфейсы. Если их разделить, вы можете просто сказать «Вот моя реализация этого интерфейса» и покончить с этим. Как я уже сказал, не то, что я делаю, но я понимаю, почему некоторые могут этого захотеть.


2

Я делаю это в проекте, и у меня это хорошо работает по следующим причинам:

  • Отдельно упакованные интерфейсы и реализации предназначены для другого варианта использования, чем для различных типов Map или Set. Нет смысла иметь пакет только для деревьев (java.util.tree.Map, java.util.tree.Set). Это просто стандартная структура данных, поэтому соберите ее вместе с другими структурами данных. Однако, если вы имеете дело с игрой-головоломкой, которая имеет действительно простой интерфейс отладки и красивый производственный интерфейс, в качестве части интерфейса у вас могут быть com.your.app.skin.debug и com.your.app. . кожа. симпатичная. Я бы не стал помещать их в один и тот же пакет, потому что они делают разные вещи, и я знаю, что прибегну к некоторому SmurfNamingConvention (DebugAttackSurface, DebugDefenceSurface, PrettyAttackSurface и т. Д.), Чтобы создать неформальное пространство имен для двух, если бы они были в одном пакете.

  • Мое решение проблемы поиска связанных интерфейсов и реализаций, находящихся в отдельных пакетах, состоит в том, чтобы принять конфигурацию именования для моих пакетов. Например, я могу разместить все свои интерфейсы в com.your.app.skin.framework и знать, что другие пакеты на том же уровне дерева пакетов являются реализациями. Недостаток в том, что это нетрадиционное соглашение. Честно говоря, через 6 месяцев я посмотрю, насколько хороша эта конвенция :)

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

  • Мое приложение использует guice и имеет очень много интерфейсов.

Вопросы разработки программ обычно связаны с преимуществами и недостатками, и универсального ответа не существует. Зачем вообще использовать эту технику в крошечной программе из 200 строк? Для меня это имеет смысл, учитывая мои другие архитектурные решения, для моей конкретной проблемы. :)


1

Я предпочитаю их в одной упаковке. Нет смысла создавать пакет специально для интерфейсов. Это особенно важно для команд, которым просто нравятся свои префиксы и суффиксы интерфейса (например, «I» и «Impl» для Java).

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


0

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

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


0

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


-1

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


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

Это справедливо, но я все же считаю, что разница в требованиях «присутствие во время сборки и во время выполнения» является убедительным аргументом в пользу упаковки отдельно. Я обновил текст, чтобы попытаться отразить это более четко.
Тайсон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.