Почему Java установил доступ к пакетам по умолчанию?


26

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

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

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

Я считаю, что именно поэтому Java использует доступ к пакетам как «по умолчанию». Но я не уверен, почему они также включили частный доступ, я уверен, что есть действительный вариант использования ...



Относится к ранее обсуждавшимся частным методам модульного тестирования; How-Do-You-Unit-Test-Private-методы . Ответ; Вы не должны
Ричард Тингл

Ответы:


25

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

Если бы они сделали «публичный» доступ по умолчанию, большинство программистов никогда бы не стали использовать что-либо еще. Результатом будет много кода для спагетти повсюду. (Потому что, если вам технически разрешено вызывать что-либо откуда угодно, зачем вообще заключать инкапсулированную логику в метод?)

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

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

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


14

Доступ по умолчанию не делает модификатор частного доступа избыточным.

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

Советы по выбору уровня доступа:

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

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

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

Конечно, вы можете иметь частные методы, и, конечно, вы можете проверить их.

Или есть какой - то способ , чтобы получить частный метод для запуска, в этом случае вы можете проверить это , что путь, или нет нет способа , чтобы получить частные бежать, и в этом случае: почему, черт возьми , вы пытаетесь проверить это, просто удалить эту чертову вещь ...


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

Вы должны связать эти классы и интерфейс в пакет по нескольким причинам, включая следующие:

  • Вы и другие программисты можете легко определить, что эти типы связаны ...
  • Вы можете разрешить типам в пакете иметь неограниченный доступ друг к другу, но по-прежнему ограничивать доступ для типов вне пакета ...

<rant "Мне кажется, я услышал достаточно скуля. Думаю, пришло время сказать громко и ясно ...">

Частные методы полезны для модульного тестирования.

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

Итак, у меня есть этот частный метод и модульные тесты, и анализ покрытия говорит мне, что есть пробел, мой частный метод не покрывается тестами. В настоящее время...

Что я получу от сохранения конфиденциальности

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

    void nonPrivateMethod(boolean condition) {
        if (condition) {
            privateMethod();
        }
        // other code...
    }

    // unit tests don't invoke nonPrivateMethod(true)
    //   => privateMethod isn't covered.

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

Хорошо, чтобы исправить разрыв, я добавляю модульное тестирование для пропущенного сценария, повторяю анализ покрытия и проверяю, что разрыв пропал. Что у меня сейчас? У меня есть новый модульный тест для конкретного использования не закрытого API.

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

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

  3. Новый тест устойчив к рефакторингу (я делаю рефакторинг частных методов? Вы держите пари!) Что бы я ни делал privateMethod, я всегда хочу тестировать nonPrivateMethod(true). Независимо от того, что я делаю privateMethod, не нужно будет изменять test, потому что метод не вызывается напрямую.

Неплохо? Вы ставите.

Что я теряю от ослабления ограничения доступа

Теперь представьте, что вместо вышеупомянутого я просто ослабляю ограничение доступа. Я пропускаю изучение кода, который использует метод, и приступаю прямо к тесту, который вызывает мой exPrivateMethod. Большой? Не!

  1. Получу ли я тест для конкретного использования не частного API, упомянутого выше? Нет: nonPrivateMethod(true)раньше не было никакого теста , и сейчас такого теста нет.

  2. Получают ли сторонние читатели шанс лучше понять использование класса? Нет. - Эй, какова цель метода, проверенного здесь? - Забудь об этом, он предназначен исключительно для внутреннего использования. - Упс.

  3. Это терпимо к рефакторингу? Ни в коем случае: что бы я ни изменил exPrivateMethod, скорее всего, это сломает тест. Переименуйте, объединитесь с другим методом, измените аргументы, и test просто прекратит компиляцию. Головная боль? Вы держите пари!

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

</ Декламация>


7
Я никогда не находил этот аргумент для тестирования частных методов убедительным. Вы все время реорганизуете код в методы по причинам, которые не имеют ничего общего с открытым API класса, и очень приятно иметь возможность тестировать эти методы независимо, без использования какого-либо другого кода. Вот почему вы рефакторинг, верно? Чтобы получить кусок независимой функциональности. Эта функциональность также должна быть проверена независимо.
Роберт Харви

Ответ @RobertHarvey расширился разглагольствованиями об этом. «Частные методы являются полезными для модульного тестирования ...»
комар

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

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

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

9

Наиболее вероятная причина: это связано с историей. Предок Java, Oak, имел только три уровня доступа: частный, защищенный, публичный.

За исключением того, что private в Oak был эквивалентом private пакета в Java. Вы можете прочитать раздел 4.10 Спецификации языка Oak (выделено мной):

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

Таким образом, на ваш взгляд, частного доступа, как известно на Java, изначально не было. Но когда у вас в пакете более нескольких классов, отсутствие приватного, как мы знаем, сейчас может привести к коллизии имен (например, java.until.concurrent имеет около 60 классов), поэтому, вероятно, они представил это.

Однако семантика доступа к пакету по умолчанию (первоначально называемая частной) не изменилась между Oak и Java.


5

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

Бывают ситуации, когда хочется, чтобы два отдельных класса были более тесно связаны, чем публичное раскрытие всего. Это похоже на «друзья» в C ++, где другой класс, объявленный как «друг» другого, может получить доступ к своим закрытым членам.

Если вы загляните во внутренние классы, такие как BigInteger, вы найдете ряд пакетов защиты по умолчанию для полей и методов (все, что является синим треугольником в списке). Это позволяет другим классам в пакете java.math иметь более оптимальный доступ к их внутренностям для определенных операций (вы можете найти эти методы, вызываемые в BigDecimal - BigDecimal поддерживается BigInteger и снова сохраняет переопределение BigInteger . То, что это уровень пакета, не является ' Это проблема защиты, потому что java.math - это запечатанный пакет, и никакие другие классы не могут быть добавлены к нему.

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

Примечательно, что модульное тестирование не было тем, о чем думали в то время, когда создавались области защиты. JUnit (среда тестирования XUnit для Java) не была задумана до 1997 года (один рассказ об этом можно прочитать на http://www.martinfowler.com/bliki/Xunit.html ). Альфа- и бета-версии JDK были в 1995 году, а JDK 1.0 - в 1996 году, хотя уровни защиты реально не снижались до JDK 1.0.2 (до этого вы могли иметь private protectedуровень защиты).

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

public class Foo {
    /* package */ int bar = 42;
    ...
}

Обратите внимание на комментарий там.

Истинная причина, по которой защита на уровне пакетов используется по умолчанию, скорее всего, потеряна в заметках по проектированию Java (я копался и не могу найти никаких статей, почему многие люди объясняют разницу, но никто не говорит «это причина «).

Так что я сделаю предположение. И это все, что есть - предположение.

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

  • Дизайнеры время от времени видели потребность в «дружеских» отношениях C ++.
  • Дизайнеры хотели получить четкие заявления public, и, privateпоскольку они оказывают наибольшее влияние на доступ.

Таким образом, private не является дефолтом и все остальное стало на свои места. Область по умолчанию была оставлена ​​по умолчанию. Это , возможно , было первой сферой , которая была создана и в интересах совместимости строгой обратных с старым кодом, осталась таким образом.

Как я уже сказал, это все предположение .


Ах, если бы только функция друга могла быть применена к элементам на индивидуальной основе, так что вы могли бы сказать, что void someFunction () может быть замечен только классом X ... это действительно увеличит инкапсуляцию!
newlogic

Ой, подождите, вы можете сделать это в C ++, нам нужно это в Java!
newlogic

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

2

Инкапсуляция - это способ сказать «вам не нужно об этом думать».

                 Access Levels
Modifier    Class   Package  Subclass   World
public        Y        Y        Y        Y
protected     Y        Y        Y        N
no modifier   Y        Y        N        N
private       Y        N        N        N

Помещая их в нетехнические термины.

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

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

@KorayTugay: посмотрите на docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html . Внутренний класс является личным. Прочитайте последний абзац.
jmoreno

0

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

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

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


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