Это четвертое издание серии. Для третьего издания нажмите здесь.

Читабельность

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

Это довольно большое изменение для Java, потому что на самом деле это означает, что public больше не является общедоступным, по крайней мере, не в том смысле, к которому мы привыкли. Ранее, если у вас был общедоступный класс в JAR, доступ к нему мог получить любой другой класс в пути к классам. Теперь, если у вас есть общедоступный класс в модуле, вам нужно задать еще один вопрос, прежде чем вы узнаете, действительно ли вы можете получить доступ к этому классу из другого модуля.

1. Первый и самый очевидный вопрос: «Вы действительно читали модуль?» То есть у вас есть требование в дескрипторе вашего модуля к другому модулю.

2. Второй вопрос: «Является ли тип в пакете экспортируемым модулем, который вам нужен?» Потому что, если у вас есть читаемость вашего модуля, вы все равно можете видеть только экспортированные пакеты.

3. Последний вопрос, на который нужно ответить: «Является ли класс общедоступным?» Потому что механизм управления доступом к Java все еще существует.

В отличие от процесса разрешения модуля, удобочитаемость не является транзитивной. Приведенный выше пример иллюстрирует это. Вопрос здесь в том, «читает ли easytext.cli, потому что он читает easytext.analysis, также и библиотечный модуль?» И ответ НЕТ. Это не транзитивно, и это по замыслу.

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

Подразумеваемая читабельность

Давайте рассмотрим пример, где важна транзитивная читабельность. Для этого мы рассмотрим модуль платформы java.sql. Для работы требуются два других модуля. Обратите внимание, что важным здесь является то, что он предоставляет интерфейс драйвера, модуль java.sql экспортирует пакет java.sql, содержащий интерфейс драйвера. То, что я показал выше, — это всего лишь небольшая часть интерфейса драйвера с одним отображаемым методом getParentLogger. Он возвращает Logger, а Logger поступает из модуля java.logging. Как это будет работать, если у нас есть модуль приложения, который требует java.sql и хочет использовать интерфейс драйвера? Здесь у нас есть наш модуль приложения, для которого требуется java.sql, но, поскольку читабельность не является транзитивной, он не может читать из java.xml и java.logging по умолчанию. Наше приложение использует интерфейс драйвера и вызывает предупреждение об этом намерении регистратора, которое было возвращено из интерфейса. Если мы попытаемся скомпилировать это приложение в этой настройке, это приведет к ошибке компиляции, которая говорит, что предупреждение в регистраторе определено в недоступном классе или интерфейсе. Что происходит, так это то, что мы можем прочитать интерфейс драйвера, потому что он исходит из модуля java.sql, и нам нужен этот модуль, но он возвращает что-то вроде регистратора, который наше приложение не может прочитать. На первый взгляд может показаться странным, что компилятор предупреждает нас о классе Logger, потому что мы нигде явно не ссылаемся на модуль logger в коде нашего приложения. Тем не менее, мы вызываем метод предупреждения, который определен в регистраторе, поэтому компилятору необходимо знать о регистраторе, чтобы проверить наш вызов предупреждения, и поэтому наше приложение также должно читать модуль ведения журнала.

Одним из решений, которое может прийти на ум, является добавление оператора require к дескриптору модуля нашего приложения в java.logging, и действительно, это решило бы проблему, но решение на самом деле не является удовлетворительным. Такое решение будет означать, что каждый модуль, использующий java.sql, будет вынужден добавлять java.logging в качестве оператора запроса. Это усложняет использование модуля java.sql, потому что вам нужно больше информации, чем просто требуется сам модуль. Было бы лучше, если бы модуль java.sql как-то передал эту информацию своим потребителям. Так что нам делать?

Модульная система предлагает специальный оператор require для именно этого варианта использования, и он называется requires Transive. Требует транзитивного делает две вещи для модуля java.sql. Во-первых, он гарантирует, что java.sql читает java.logging и java.xml так же, как и раньше, но, кроме того, он предлагает так называемую подразумеваемую читабельность для потребителей java.sql, что означает, что модули, требующие java .sql теперь также автоматически считывает файлы java.logging и java.xml. В модуле приложения теперь мы можем вызывать любой метод любого общедоступного класса java.sql, не беспокоясь о транзитивных зависимостях.

С одной стороны, инкапсуляция кода в модули — это хорошо, но с другой стороны, это внесет некоторое усложнение в наш код.

Ограничения инкапсуляции
1.
Зависит от реализации, а не от интерфейса
2. Классы реализации необходимо экспортировать
3. Тесная связь между модулями
4. Не расширяемость

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

Допустим, у нас есть четыре разных реализации некоторых алгоритмов анализа, показанных выше как 1, 2, 3 и 4. Каждая реализация живет в своем собственном модуле, потому что ее можно использовать независимо, и мы можем скрыть детали реализации от других модулей. Пока все хорошо, скажете вы, но эта картина неполная. Нам также нужно подумать о потребительских модулях.

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

Здесь есть вторая, возможно, большая проблема. Как вы можете видеть, чтобы использовать анализ с модулями cli, необходимо создать экземпляр класса анализа модуля и то же самое для всех других модулей анализа. На первый взгляд это выглядит нормально, но если подумать, это не совсем то, что вам нужно, потому что всякий раз, когда добавляется новая реализация, вам нужно знать о точном классе реализации в ваших потребительских модулях, чтобы создавать экземпляры и вызывать методы.< br /> Более естественным способом сделать это было бы введение интерфейса, который унифицирует функциональность различных реализаций анализа.
Все эти проблемы показывают нам, что мы достигаем пределов того, что инкапсуляция может сделать для нас в этих сценарии.

Конечно, не существует неразрешимых проблем, и мы рассмотрим их в следующей главе.

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

источник:
https://www.geekboots.com
https://www.pluralsight.com