Подскажите пожалуйста какой подход являтся правильным при проектировании Java библиотек с множеством внутренних packages?
Сейчас работаю над библиотекой с API для предоставления некоторого фукнционала клиентам.
Текущая бибилиотека имеет много packages включая вложенные и все классы объявлены как public включая те которые ну никак не должны быть доступны клиенту. Сейчас скырытие этих классов обеспечивается тем что они просто не упомянуты в документации. Раз их нет в документации то они "скрыты"!
Даже если я объявляю свой класс как не public проблема в том что мой класс становится не доступным другим внутренним модулям и получается что мне приходитс делать его public и следовательно вновь доступным клиенту.
Сейчас я добавляю новую функицональность в эту библиотеку и хотел бы скрыть реализацию ( хотя бы эту новую часть )
Опять единственный вариант я вижу как то изолировать всю мою функциональность в одном package, но все равно получается что публичный интерфейс этого package будет доступен и клиенту.
Есть какие то еще механизмы языка или общепринятые подходы в таких случаях?
Я посмотрел несколько opensource библиотек и в большинстве случаев в все классы в packages объявлены как public.
Сейчас мой код на Java 11.
Спасибо!
Re: Скрытие имплементации при проектировании Java библиотек
Механизм называется java modules. Вы указываете, какие пакеты экспортируются. Правда модулями далеко не все пользуются, а если не пользуются, то всё это игнорируется.
Второй вариант это использовать два jar. Первый jar клиент подключает как обычно, второй jar клиент подключает как runtime, в итоге при компиляции к классам второго jar он не будет иметь доступа.
Понятно, что если клиент захочет, то получит. Впрочем он и так получит, если захочет, через reflection или редактирование байткода (добавить модификатор public несложно) или ещё как-нибудь.
Если прям совсем не хотите, чтобы туда даже смотрели, можете обфусцировать этот пакет. Формально он останется public, но там никто ничего не поймёт.
А так — в рамках Java 8 ничего сделать нельзя. Если нужно использовать класс из пакета, он должен быть public. Если он public, то его может использовать кто угодно.
Здравствуйте, salvequick, Вы писали:
S>Есть какие то еще механизмы языка или общепринятые подходы в таких случаях?
Общепринятый — документация или здравый смысл. По части пакетов/классов/методов разработчики гарантируют обратную совместимость при доработках, а по остальным — нет.
Общепринятый работает, только если за проблемы совместимости отвечают потребители кода, что бывает не всегда. Например, при обновлении платформы потеря совместимости с модулями — это проблемы платформы, а не модулей.
Re[2]: Скрытие имплементации при проектировании Java библиотек
Спасибо!
vsb>Понятно, что если клиент захочет, то получит. Впрочем он и так получит, если захочет, через reflection или редактирование байткода (добавить модификатор public несложно) или ещё как-нибудь.
Здесь я даже не имеюю ввиду что будут испольщовать рефлексию или или менять байткод.
Было бы достаточно если нельзя было бы создать эти классы обычными способами.
vsb>Если прям совсем не хотите, чтобы туда даже смотрели, можете обфусцировать этот пакет. Формально он останется public, но там никто ничего не поймёт.
Это не требуется.
Re[3]: Скрытие имплементации при проектировании Java библиот
Здравствуйте, salvequick, Вы писали:
S>Было бы достаточно если нельзя было бы создать эти классы обычными способами.
Ну в качестве странноватой, но вроде рабочей идеи — в конструкторе можно проверять стектрейс и убеждаться, что класс создаётся из вашего кода, а не из чужого.
Ещё одна странноватая идея — объявить класс abstract. Тогда его инстанцировать не получится. В другом пакете объявить уже package-private наследника и инстанцировать. Клиент, конечно, сможет сделать то же самое, но это уже как бы лишние действия, случайно такое не сделать.
Здравствуйте, vsb, Вы писали:
vsb>Ну в качестве странноватой, но вроде рабочей идеи — в конструкторе можно проверять стектрейс и убеждаться, что класс создаётся из вашего кода, а не из чужого.
Вы имеете ввиду вызвать Thread.currentThread().getStackTrace() и смотреть что последний элемент в стектрейсе принадлежит какму то из моих пакетов?
( т.е внутреннее создание )
Думаю что вариант с абстрактным классом мне больше подойдет, но любопытства ради хотелось бы понять.
vsb>Ещё одна странноватая идея — объявить класс abstract. Тогда его инстанцировать не получится. В другом пакете объявить уже package-private наследника и инстанцировать. Клиент, конечно, сможет сделать то же самое, но это уже как бы лишние действия, случайно такое не сделать.
Вот это кстати возможно рабочая идея для меня!
Вы правы пользователь сможет их создать, но для этого нужно наследоваться и случайно это не сделать!
Спасибо!
Re[5]: Скрытие имплементации при проектировании Java библиот
Здравствуйте, salvequick, Вы писали:
s> Вы правы пользователь сможет их создать, но для этого нужно наследоваться и случайно это не сделать!
А в чём цель-то? Зачем это всё? Поместить классы в какой-нибудь package и назвать impl или internal — если пользователь заиспользует случайно, так сам виноват, ибо знал что творил. Может для тестов надо, да пусть ради богу. Если твоё очередное изменение поломает его код, то ему чинить, не твои проблемы.
Здравствуйте, salvequick, Вы писали:
vsb>>Ну в качестве странноватой, но вроде рабочей идеи — в конструкторе можно проверять стектрейс и убеждаться, что класс создаётся из вашего кода, а не из чужого. S>Вы имеете ввиду вызвать Thread.currentThread().getStackTrace() и смотреть что последний элемент в стектрейсе принадлежит какму то из моих пакетов? S>( т.е внутреннее создание ) S>Думаю что вариант с абстрактным классом мне больше подойдет, но любопытства ради хотелось бы понять.
Примерно да. Если Java 9+, то лучше использовать класс StackWalker, он более производительный, когда полный стек не нужен (а в данном случае не нужен).