Полезна ли область действия уровня пакета Java?


11

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

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

Например, у меня может быть модель Миссии, и я хочу, чтобы только другие инструменты Миссии, такие как мои сервисы миссии, использовали некоторые методы. Тем не менее, я получаю в качестве своих пакетов Missions.models и Missions.services, так что MissionModel и MissionService - это не одна и та же область действия пакета. Никогда не кажется, что существует ситуация, когда пакеты надлежащим образом содержат вещи, для которых я хотел бы иметь повышенные разрешения, и не включали в себя множество вещей, которые я не хотел бы иметь этих разрешений; и редко я чувствую преимущество определения объема пакета, когда метод оправдывает изменение архитектуры моего проекта, чтобы поместить все в один пакет. Часто либо Аспекты, либо какая-то инверсия управления оказываются лучшим подходом к любой проблеме, которую я кратко рассматривал для определения объема пакета.

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

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


Мысленный эксперимент: как бы выглядели Java-программы, если бы у вас не было пакетов? Возможно, вы не смотрели и не работали с большими Java-программами.
Роберт Харви

Посмотрите на код для java.util.Stringи java.util.Integer.


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

2
Меня совсем не убедил дубликат. Этот вопрос задает вопрос "Для чего предназначен пакет?" в то время как другой спрашивает (среди прочего): «Заданная область пакета является значением по умолчанию, для чего нужна частная область?» Плюс принятый ответ на обмане и принятый ответ здесь делают совершенно разные моменты.
Ixrec

Ответы:


2

Во всем java.util.*пакете есть случаи, когда код написан с защитой на уровне пакета. Например, этот бит java.util.String- конструктор в Java 6 :

// Package private constructor which shares value array for speed.
String(int offset, int count, char value[]) {
    this.value = value;
    this.offset = offset;
    this.count = count;
}

или этот метод getChars :

/** Copy characters from this string into dst starting at dstBegin. 
    This method doesn't perform any range checking. */
void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, offset, dst, dstBegin, count);
}

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

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

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

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


1
Я просмотрел java.util и вижу это. Тем не менее, я также заметил, что это большая библиотека. Сейчас кажется, что большие библиотеки редко пишутся без использования подпакетов; и это те подразделения подпакета, которые приводят к ограничениям. Я не говорю, что java.util ошибочен, но кажется, что они могли избежать неприятностей с большим пакетом только потому, что у них были ЧРЕЗВЫЧАЙНО хорошие программисты, которым они могли доверять, чтобы делать все правильно с довольно ограниченной инкапсуляцией в пакете; Я чувствовал бы себя обнаженным, полагая, что большой потенциал может навредить средним разработчикам, которые могут работать с такими же пакетами, что и я.
dsollen

1
@dsollen Если вы покопаетесь в Spring, Hibernate или похожих больших библиотеках, вы найдете похожие вещи. Есть моменты, когда вам нужно более тесное соединение. Один должен иметь возможность проверки кода представленные материалы и убедитесь , что все правильно. Если вам не нужна более тесная связь, или вы вообще не доверяете другим кодировщикам, тогда открытые поля, или упакованные или защищенные поля или методы не всегда подходят. Однако это не значит, что это бесполезно.

Короче говоря, я бы соблазнился сделать более ограниченные пакеты и не беспокоиться об уровне оптимизации (который является пустой тратой в 95% проектов) для моих повседневных потребностей. Таким образом, мой следующий вопрос: действительно ли область уровня пакета - это очень специфический потенциал оптимизации, используемый только в некоторых широко используемых массовых API? То есть, только великие люди будут нуждаться или хотят ослабить защиту до такой степени? Есть ли причины, по которым хорошие программисты, работающие над «стандартным» API с меньшими пользовательскими базами, могли бы использовать это?
dsollen

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

2
@dsollen это не о производительности (хотя это одна из причин выставления реализации), а скорее инкапсуляция. Вы делаете это, чтобы избежать повторения кода, Integer.toString()и String.valueOf(int)можете делиться кодом, не раскрывая его миру. Эти java.utilклассы должны работать сообща , чтобы обеспечить больше всей функциональности а не отдельные классы, которые полностью острова к себе - они вместе в пакет и доля функциональных возможностей реализации через уровень пакета обзорного.

5

Иногда я увеличиваю видимость приватных или защищенных методов для упаковки, чтобы позволить junit-тесту получить доступ к некоторым деталям реализации, к которым нельзя обращаться извне.

поскольку тест junit имеет тот же пакет, что и тестируемый элемент, он может получить доступ к этим деталям реализации

пример

  public class MyClass {
    public MyClass() {
        this(new MyDependency());
    }

    /* package level to allow junit-tests to insert mock/fake implementations */ 
    MyClass(IDependency someDepenency) {...}
  }

Это спорно, если это «хорошее» использование или нет, но это прагматичное


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

Я никогда не делаю это с методами, но это хорошая возможность использовать для вставленных зависимостей. Например, при тестировании EJB-объектов внедренные EntityManager могут быть смоделированы путем установки EM уровня пакета с объектом Mock, в который он во время выполнения вставлялся бы сервером приложений.
15:00

Зачем переходить protectedв пакетную сферу? protected уже предоставляет доступ к пакету. Это немного странно, но я думаю, что они не хотят требовать составных объявлений области видимости, таких как protected packagescope void doSomething()(для которых также требуется новое ключевое слово).
tgm1024 - Моника подверглась жестокому обращению

2

Это не так уж и полезно, особенно по умолчанию, в мире, где люди склонны использовать точечные имена пакетов, как если бы они были подпакетами. Поскольку в Java нет такого понятия, как подпакет, поэтому xyinternals не имеет больше доступа к xy, чем к ab. Имена пакетов одинаковы или различны, частичное совпадение ничем не отличается от отсутствия общего.

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

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

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


Я нахожу комментарий java 9 интересным. Я думаю, что простое умение распознавать иерархии пакетов и каким-то образом определять область действия пакета относительно некоторого родительского пакета (используя аннотации?) Было бы неплохо.
dsollen

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

2

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

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

**MissionManager.jar** - an application which uses the Java Service Provider Interface to load some implementation of missions, possible provided by a 3rd party vendor.

**missions-api.jar** - All interfaces, for services and model
    com...missions
        MissionBuilder.java   - has a createMission method
        Mission.java - interface for a mission domain object
    com...missions.strategy
        ... interfaces that attach strategies to missions ...
    com...missions.reports
        .... interfaces todo with mission reports ....

   **MCo-missionizer-5000.jar** -  provides MCo's Missionizer 5000 brand mission implementation.
       com...missions.mco
                  Mission5000.java - implements the Mission interface.
       com...missions.strategy.mco
              ... strategy stuff etc ...

Используя уровень пакета в Mission5000.java, вы можете быть уверены, что ни один другой пакет или компонент, такой как MissionManager, не может быть тесно связан с реализацией миссии. То есть, только компонент, предоставленный «третьей стороной» от «M co», фактически создает там собственную специальную фирменную реализацию Миссии. Менеджер миссий должен вызвать MissionBuiler.createMission (...) и использовать определенный интерфейс.

Таким образом, если компонент реализации Mission будет заменен, мы знаем, что разработчики MissionManager.jar не обошли API и напрямую использовали реализацию MCo.

Следовательно, мы можем обменять MCo-missionizer5000.jar на конкурирующий продукт. (Или, возможно, макет для юнит-тестирования менеджера миссии)

И наоборот, MCo может скрыть свои секреты коммерческой миссии.

(Это также связано с обеспечением идей нестабильности и абстрактности в компонентах.)


-1

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

Вместо этого мы используем наши собственные правила доступа, основанные на иерархии пакетов и предварительно определенных именах пакетов.

Если вы заинтересованы в деталях, Google для "CODERU-Правила".

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.