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




Вы говорите о частной защите пакетов в Java? Это защита, которая действует по умолчанию для членов класса. Иногда это полезно, если у вас есть классы, которые тесно взаимодействуют таким образом, что для отображения дополнительной информации или методов требуется дополнительная информация.
Допустим, у вас есть класс Sink, и несколько классов могут писать в него. У Sink есть открытый метод, который принимает базовые типы данных, и закрытый метод пакета, который принимает необработанные байтовые массивы. Вы не хотите делать этот метод общедоступным, потому что считаете его слишком низкоуровневым для пользователей, но вы хотите, чтобы другие ваши классы (в пакете Sink), пишущие в этот Sink, использовали его. Таким образом, вы делаете метод, принимающий пакет байтового массива, частным, и классы вашего пакета, такие как ByteStreamSource, могут его использовать. Теперь ваша защита выглядит так:
User Code using the package User
------ | -----------------------------|----- package public Interface
| |
Sink <- package priv. iface -> Sources
Частный интерфейс пакета ортогонален общедоступному интерфейсу, установленному общедоступными методами. Приватность пакета увеличивает инкапсуляцию, потому что она побуждает вас нет сделать общедоступным то, что не должно быть общедоступным. Это похоже на ключевое слово friend в C++ и ключевое слово internal в C#.
С одной стороны, его можно использовать для классов реализации. Например, EnumSet - абстрактный класс, и в документации не показаны реализующие классы. Почему? Потому что существует два реализующих класса - один для перечислений с 64 или менее элементами и один для 65 или более - и вам действительно не нужно знать, какой из них вы используете. Фактически, вам даже не нужно знать, что существует более одной реализации. Фактически, вам даже не нужно знать, что EnumSet является абстрактным - вам просто нужно знать, что вы можете вызвать один из статических методов и получить обратно Set.
В этом случае могло быть достаточно частных внутренних классов (хотя и громоздких, особенно для больших классов). Но иногда другим классам в пакете может потребоваться доступ к таким деталям реализации. protected может работать, но тогда они также будут открыты для подклассов.
Короче говоря, он охватывает область инкапсуляции, которая не обрабатывается другими тремя уровнями защиты.
У меня есть объекты, которым нужен доступ друг к другу. Внутри пакета они могут вызывать эти защищенные методы, но когда кто-то пытается получить доступ к этим защищенным методам, они запрещаются.
На мой взгляд, это базовый механизм защиты, и иерархия защиты была бы хорошей функцией.
Внутренний в C# означает «общедоступный в сборке = общедоступный в файле jar».
Защита по умолчанию, то есть защита «пакета» означает, что она является частной для пакета, но может «видеть» все, что не является частным для класса в пакете. Одно из применений - вспомогательные классы для пакета; скажем, у вас есть класс, который управляет объединенным ресурсом, который вы не хотите делать видимым, вы можете поместить его в класс защиты по умолчанию. Теперь он может использоваться всем в пакете, и он может получить доступ к любым защищенным внутренним компонентам других классов, но он не является частью видимого для пользователя пакета.
В настоящее время пакеты часто используются для моделирования «программных компонентов», то есть пакет - это группа классов, так или иначе связанных. Поскольку «общедоступные» методы определяют внешний интерфейс программного компонента, а «частные» методы / члены являются деталями реализации класса, видимость «пакета» по умолчанию сводится к внутренним методам компонента.
Аналогией могут быть «дружественные» методы в C++.
Защита по умолчанию в Java также используется для повышения производительности.
Внутренним классам разрешен доступ к закрытым членам содержащего их класса, но это реализуется с помощью обфусцированных методов доступа. Прежде чем компилятор JIT оптимизирует это (или в средах без JIT, таких как J2ME), это приведет к небольшому снижению производительности. Объявление необходимых методов с защитой по умолчанию устраняет необходимость в этих вызовах методов доступа, но не требует, чтобы члены были полностью общедоступными.
Имейте в виду, что это важно только в тех случаях, когда известно, что производительность критична, и когда вы не можете рассчитывать на то, что JVM решит проблему за вас. Но, по крайней мере, это не вредит удобочитаемости / удобству обслуживания в такой степени, как многие другие микрооптимизации.
Изменение области видимости ради непроверенного прироста производительности - всегда плохая идея.
Я бы сказал "обычно" плохая идея. Если вы пишете код для низкопроизводительной платформы (телефона?), Это может быть необходимо.
Можете ли вы действительно задокументировать случай, когда это вызывает проблемы с производительностью? Я не думаю, что это невозможно, но мне это кажется маловероятным.
Я думаю, что в принципе защита на уровне пакетов имеет большой смысл. Он позволяет вам создавать что-то близкое к модулю (пакету), но раскрывать только основные классы.
Например, многие из моих проектов работают с общедоступными интерфейсами и «скрытыми» классами, которые их реализуют. Пользователи используют фабрики и получают ссылку на интерфейс, который соответствует «скрытой» конкретной реализации. С защитой пакетов я теоретически должен иметь возможность открывать только интерфейсы и фабрику.
К сожалению, поскольку защита пакетов не применяется к подпакетам, она не совсем подходит тому, как я работаю. Мне нравится использовать другие пакеты и, в частности, подпакеты для моих «внутренних» вещей (так же, как организовано Eclipse). В результате мой пакет не может получить доступ к объектам, защищенным на уровне пакета в подпакете. Я действительно хочу, чтобы это когда-нибудь изменилось.
Я согласен с модификатором видимости пакетов и подпакетов; Я был вынужден сделать методы для подпакетов общедоступными, потому что это было меньшим злом, чем перемещение классов подпакетов в родительский пакет.
И у меня есть один пакет, в котором набор из 6 или около того классов входит в подпакет, но методы, которые им нужны, абсолютно не должны быть общедоступными - это тот случай, когда я действительно хотел бы, чтобы я мог явно объявить другой пакет как друг основного.
Как правило, можно использовать защиту по умолчанию / пакет, чтобы сделать «общедоступные» классы и переменные более ограниченными.
Вы экспортируете только несколько методов из своего класса (если вы хороший программист), помечая их общедоступными, сохраняя при этом как можно более частные.
Защита пакетов позволяет вам экспортировать только несколько классов из вашего пакета, оставив остальные приватными.
Теоретически это отличная идея, на практике вы ее почти никогда не видите.
Я считаю, что последнее утверждение неверно; У меня есть много примеров методов, защищенных законными пакетами. Это особенно. Значение true, когда пакет выполняет тесно связную функцию (в отличие от того, чтобы быть набором связанных классов).
Я тоже написал довольно много - но я почти никогда не видел их в дикой природе. Я согласен, что это отличная идея.
На мой взгляд, доступ к методам и полям с закрытым пакетом (по умолчанию) бесполезен. Фактически, это хуже, чем бесполезно, это вредно, поскольку обычно используется для предоставления доступа к какому-то члену, который должен быть приватным, но не по соображениям удобства.
Тем не менее, частные классы пакета находятся полезны. Вы можете захотеть предоставить реализацию интерфейса, используя частный класс пакета. Можно объявить фабричный метод для возврата объекта, реализующего данный интерфейс. В этой ситуации не важно знать, какой класс обеспечивает реализацию интерфейса. Сделав пакет класса частным, он не является частью общедоступного API и, следовательно, может быть изменен или заменен в будущих версиях.
В Oak, языке, который позже стал Java, было всего 3 уровня доступа.
Если бы я перепроектировал Java сейчас, я бы избавился от текущего значения по умолчанию (package-private) и сделал частным по умолчанию. Частным классом будет тот, который является частным в пределах его охватывающей области (пакета), что будет работать точно так же, как уже работают классы с частным пакетом. а также будет более согласованным с использованием «частного» для членов.
интересная ссылка, спасибо. но они, кажется, хвалят текущие 4 уровня доступа. в дубе были общедоступный, защищенный и пакетный частный (по умолчанию), видимо (слух о дубе первое время :)). java оставил значение по умолчанию там, где он был, и ввел частный как более ограничительный.
Да, Oak определенно не понял этого, потому что ему не хватало настоящего «приват».
-1 за утверждение о том, что доступ к пакету бесполезен, сохраненный в последнюю секунду со счетчиком +1 за пожелание приватности по умолчанию и подразумевающий, что следует обычно объявлять вещи как приватные и открывать их только тогда, когда это необходимо.
@Software Monkey, в каких ситуациях вы используете методы, видимые для пакета (а не классы)? Каждый раз, когда я использовал метод, видимый для пакета, я всегда чувствовал себя взломанным.
Причина, по которой никто не использует доступ на уровне пакетов, вероятно, это психологический эффект. Каждое введение в java проповедует инкапсуляцию, которая понимается как объявление всех полей закрытыми. Частный доступ очень часто бывает слишком узким, поэтому необходимо добавить общедоступный геттер / сеттер. Затем этот шаблон повторяется во всей кодовой базе: везде закрытые поля и общедоступные методы. По иронии судьбы, этот шаблон подрывает инкапсуляцию, поскольку вся реализация по существу скомпонована парами геттер / сеттер.
Класс - это, прежде всего, единица распределения памяти, и во многих случаях он слишком мал, чтобы обеспечить осмысленный интерфейс для остальной части программы. Гораздо лучше использовать пакеты в качестве единицы инкапсуляции и сосредоточиться на предоставлении им четко определенных общедоступных интерфейсов.
Суперпакеты в 1.7 могут изменить правила игры.
Я думаю, что этим ответом, к сожалению, пренебрегают. Очень проницательно, ИМХО.
Я знаю ветеранов Java 5-10 + лет, которые не понимают, что protected подразумевает частный доступ к пакету. Одно это для меня делает закрытый пакет ужасной языковой функцией. Лично я не думаю, что есть какие-то веские причины использовать частный пакет в любом случае. Рассмотрим варианты использования:
Сравните это с защищенными методами, которые полностью оправданы при разработке классов для расширения. Я видел случаи, когда программисты нечаянно использовали защищенные методы в несвязанных классах в одном пакете только потому, что они попадают в списки автозаполнения.
И предотвратить это абсолютно невозможно.
C# имеет лучшую систему доступа, поскольку защищенный не подразумевает внутренний доступ. Но я считаю это - вместе с изменяемым классом Date - довольно серьезным недостатком.
-1 за то, что, на мой взгляд, явно абсурдно. Доступ к пакету имеет огромную применимость для пакета, который выполняет единственную общую цель - скажем, пакет сопоставления с шаблоном, такой как регулярное выражение.
@Software Monkey Я не уверен, что его утверждение абсурдно;) Cletus аргументы в пользу стиля кода, используемого множеством вещей apache. Мне не нравятся файлы классов из 3000 строк, к которым это приводит, но они намного лучше скрывают личные вещи. Я думаю, что со многими классами, защищенными пакетами, это намного лучше.
«Я знаю, как работает защита на уровне пакетов в java ... и, похоже, никто ее не использует».
Что они используют?
Делают ли они все свои классы общедоступными?
Принцип Бремени принимает две формы.
Сильная форма утверждает, что бремя преобразования набора сущностей зависит от количества преобразованных сущностей. Слабая форма утверждает, что максимальное потенциальное бремя преобразования набора сущностей является функцией максимального потенциального числа преобразованных сущностей.
Международная организация по стандартизации определяет инкапсуляцию как свойство, согласно которому информация, содержащаяся в объекте, доступна только через взаимодействия на интерфейсах, поддерживаемых объектом.
На более высоком уровне абстракции программные модули также могут быть инкапсулированы в подсистемы, при этом информация, содержащаяся в подсистеме, доступна только через общедоступные программные модули, содержащиеся в подсистеме.
Бремя создания или модификации любой программной системы зависит от количества созданных или измененных программных модулей.
Программные блоки, которые зависят от конкретной измененной программной единицы, имеют более высокую вероятность воздействия, чем программные блоки, которые не зависят от измененной программной единицы.
Максимальное потенциальное бремя, которое может наложить модифицированный программный модуль, - это воздействие на все программные модули, которые от него зависят.
Уменьшение зависимостей от модифицированного программного модуля, таким образом, снижает вероятность того, что его обновление повлияет на другие программные модули, и, таким образом, снижает максимальное потенциальное бремя, которое может наложить этот программный модуль.
Таким образом, уменьшение максимального потенциального количества зависимостей между всеми программными модулями в системе снижает вероятность того, что воздействие на конкретный программный модуль вызовет обновления для других программных модулей, и, таким образом, снижает максимальную потенциальную нагрузку на все обновления.
Теория инкапсуляции показывает, как использовать инкапсуляцию для уменьшения максимального потенциального количества зависимостей между всеми программными модулями.
Таким образом, теория инкапсуляции показывает, как использовать инкапсуляцию для смягчения слабой формы принципа бремени.
В Java создание закрытого пакета класса является одним из ключевых механизмов уменьшения максимального потенциального числа зависимостей в системе и, таким образом, снижения максимально возможной нагрузки на любую модификацию программного обеспечения этой системы.
Однако вы упоминаете, что это не используется в читаемом вами коде.
Звучит ... странно.
Есть два хороших варианта использования видимости на уровне пакетов (по моему опыту):
1) Определение «внутренних» классов в публичном API. Обычно вы определяете свои интерфейсы и основные фабрики как общедоступные, а «внутренние» реализации - как уровень пакета. Затем общедоступные фабрики могут создавать классы реализации на уровне пакета и возвращать их как экземпляры общедоступных интерфейсов. Это прекрасно позволяет пользователям получать доступ только к тому, что им нужно.
Обратной стороной является то, что вам нужно иметь все это в одном пакете, что почти никогда не бывает хорошей идеей для любого API разумного размера. Мы надеемся, что JSR 294 / modules / Проект Jigsaw в Java 7 предоставит альтернативу, указав новый модификатор видимости (module), который можно использовать для доступа к классам внутри модуля в пакетах, не делая их видимыми вне модуля. Вы можете найти пример того, как это будет работать в этой статье.
2) Модульное тестирование - еще один распространенный вариант использования. Часто вы увидите дерево src и тестовое дерево, и все, что в противном случае было бы частным, - это уровень пакета, так что модульные тесты в одном (параллельном) пакете могут получить доступ к скрытым в противном случае методам для проверки или управления состоянием.
В основном для модульного тестирования, когда исходные и тестовые файлы находятся в одном пакете, тесты могут обращаться к внутренним компонентам класса, не раскрывая его.
В настоящее время я обсуждаю многие из этих вопросов в уме, и это хорошее обсуждение. Я решил сделать все частные вещи такими, какими они должны быть, но затем я пишу массу размышлений для модульного тестирования кода с частной областью видимости. Это, я думаю, чистый объектно-ориентированный способ сделать это. Однако я обнаружил, что можно запечатать файл jar, чтобы область пакета была ограничена внутри модуля (jar). Это, на мой взгляд, делает объем пакета намного более приемлемым. Возможно, я захочу нарушить хорошую инкапсуляцию в обмен на сокращение моего кода модульного теста до доли того, чем он был бы. Мысль заключалась в том, что если бы кто-то работал в одной и той же библиотеке и в одном пакете, он был бы достаточно близок с кодовой базой, чтобы не получать доступ к членам области действия пакета извне класса без разумного обсуждения дизайна. Это все еще трудный вызов. Было бы неплохо иметь обозначение типа дружественного класса для этого типа экземпляра, чтобы мой код кода модульного теста имел доступ к моему частному коду, но не был доступен другим классам. Я только что запечатал свои файлы jar в этом проекте через maven:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<archive>
<index>true</index>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<sealed>true</sealed>
</manifestEntries>
</archive>
</configuration>
</plugin>
Теперь я спорю о том, чтобы добавить кучу кода отражения в свои модульные тесты.
Я все еще нахожусь на грани дискуссии. Область действия пакета действительно довольно открыта, особенно если у вас есть много разработчиков, которые работают с базой кода.
Возможно, доступ на уровне модуля? Я думаю, вероятно, в JDK7.