Какая польза от защиты на уровне пакетов в Java? - PullRequest
22 голосов
/ 31 декабря 2008

Я знаю, как работает защита на уровне пакетов в Java. Я читаю много кода (включая множество материалов с открытым исходным кодом), и никто, кажется, не использует его. Мне кажется, что весь уровень защиты слегка неисправен (в любой день недели у меня будет встроенный c #).

Существуют ли какие-либо законные случаи использования в реальном мире, которые обычно используются?

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

Ответы [ 15 ]

33 голосов
/ 02 января 2009

Существует два хороших варианта использования видимости на уровне пакета (по моему опыту):

1) Определение «внутренних» классов в публичном API. Обычно вы определяете свои интерфейсы и основные фабрики как публичные, а «внутренние» реализации - как уровень пакетов. Затем общедоступные фабрики могут создавать классы реализации уровня пакета и возвращать их как экземпляры общедоступных интерфейсов. Это позволяет пользователям получать доступ только к тому, что им нужно.

Недостатком является то, что вы должны иметь все эти вещи в одном пакете, что почти никогда не является хорошей идеей для любого API разумного размера. JSR 294 / modules / Project Jigsaw в Java 7, будем надеяться, предоставит альтернативу, указав новый модификатор видимости (module), который можно использовать для доступа к классам внутри модуля через пакеты не делая их видимыми снаружи модуля. Вы можете найти пример того, как это будет работать в этой статье .

2) Модульное тестирование является другим распространенным вариантом использования. Часто вы видите дерево src и дерево тестов, и все, что в противном случае было бы закрытым, вместо этого является уровнем пакета, так что модульные тесты в том же (параллельном) пакете могут получить доступ к скрытым в противном случае методам для проверки или манипулирования состоянием.

12 голосов
/ 31 декабря 2008

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

Скажем, у вас есть класс Sink, и несколько классов могут писать в него. У Sink есть открытый метод, который принимает основные типы данных, и закрытый метод пакета, который принимает необработанные байтовые массивы. Вы не хотите делать этот метод общедоступным, потому что считаете его слишком низкоуровневым для своих пользователей, но вы хотите, чтобы другие классы (в пакете Sink) записывали его в этот Sink. Таким образом, вы делаете метод, принимающий пакет байтового массива, закрытым, и классы вашего пакета, такие как ByteStreamSource, могут его использовать. Теперь ваша защита выглядит так:

     User   Code using the package   User
------ | -----------------------------|----- package public Interface
       |                              |
    Sink <-   package priv. iface  -> Sources

Закрытый интерфейс пакета ортогонален общедоступному интерфейсу, установленному общедоступными методами. Приватность пакетов увеличивает инкапсуляцию, потому что это поощряет вас , а не , публиковать то, что не должно быть публичным. Это похоже на ключевое слово friend в C ++ и ключевое слово internal в C #.

8 голосов
/ 31 декабря 2008

Может использоваться для классов реализации, с одной стороны. Например, EnumSet является абстрактным классом, и никакие реализующие классы не показаны в документах. Зачем? Потому что есть два реализующих класса - один для перечислений с 64 или менее элементами и один для 65 или более - и вам действительно не нужно знать, какой из них вы используете. На самом деле, вам даже не нужно знать, что существует более одной реализации. На самом деле, вам даже не нужно знать, что EnumSet является абстрактным - вам просто нужно знать, что вы можете вызвать один из статических методов и получить Set обратно.

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

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

5 голосов
/ 31 декабря 2008

Я думаю, что в принципе защита на уровне пакета имеет большой смысл. Он позволяет вам создавать что-то близкое к модулю (пакету), но раскрывать только основные классы.

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

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

3 голосов
/ 31 декабря 2008

Другое использование защиты по умолчанию в Java для повышения производительности.

Внутренним классам разрешен доступ к закрытым членам их содержащего класса, но это реализуется через запутанные методы доступа. До того, как JIT-компилятор оптимизирует это (или в средах без JIT, таких как J2ME), это вызывает небольшое снижение производительности. Объявление необходимых методов с защитой по умолчанию устраняет необходимость в этих вызовах методов доступа, но не требует полной открытости членов.

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

3 голосов
/ 31 декабря 2008

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

2 голосов
/ 31 декабря 2008

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

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

Суперпакеты в 1.7 могут изменить правила игры.

2 голосов
/ 31 декабря 2008

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

Однако приватные классы полезны. Возможно, вы захотите предоставить реализацию интерфейса, используя закрытый класс пакета. Может быть объявлен фабричный метод для возврата объекта, который реализует данный интерфейс. В этой ситуации не важно знать, какой класс предоставляет реализацию для интерфейса. Делая класс package-private, он не является частью общедоступного API и поэтому может быть изменен или заменен в будущих версиях.

В Oak, языке, который впоследствии стал Java, было только 3 уровня доступа .

Если бы я сейчас перепроектировал Java, я бы избавился от текущего значения по умолчанию (package-private) и сделал бы private по умолчанию. Закрытый класс - это тот, который является закрытым в пределах своей включающей области (пакета), который будет работать точно так же, как классы частного пакета и будут более совместимы с использованием «частного» для членов.

1 голос
/ 02 августа 2012

В настоящее время я думаю о многих из этих проблем, и это хорошая дискуссия. Я решил сделать все личные вещи приватными, какими они должны быть, но затем я пишу тонну размышлений о модульном тестировании частного кода. Это, я думаю, чистый ОО способ сделать это. Тем не менее, я обнаружил, что можно запечатать файл 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>

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

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

1 голос
/ 04 января 2009

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

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