Я никогда не находил это проблемой. Обходной путь (если вы хотите так его назвать) называется хороший дизайн API .
Если вы хорошо спроектируете свою библиотеку, то вы почти всегда можете сделать следующее:
- Поместите основной публичный API в один пакет, например. «my.package.core» или просто «my.package»
- Поместите вспомогательные модули в другие пакеты (в соответствии с логическими группировками), но предоставьте каждому свой собственный общедоступный набор API (например, фабричный класс, такой как "my.package.foobarimpl.FoobarFactory")
- Основной открытый пакет API использует только открытый API вспомогательных модулей
- Ваши тесты также должны выполняться в первую очередь на общедоступных API-интерфейсах (поскольку это то, что вас волнует с точки зрения регрессий или функциональности)
Для меня «правильный уровень инкапсуляции» для пакета заключается в том, чтобы предоставить достаточно публичного API, чтобы ваш пакет мог эффективно использоваться в качестве зависимости. Не больше и не меньше. Не должно иметь значения, используется ли он другим пакетом в той же библиотеке или внешним пользователем. Если вы разрабатываете свои пакеты в соответствии с этим принципом, вы увеличиваете вероятность эффективного повторного использования.
Создание частей пакета «глобально доступным» на самом деле не приносит никакого вреда, если ваш API достаточно хорошо разработан. Помните, что пакеты не являются объектными экземплярами, и в результате инкапсуляция не имеет большого значения: сделать элементы пакета общедоступными обычно намного менее вредно, чем раскрытие внутренних деталей реализации класса (что я согласен почти всегда должен быть приватным / защищенным).
Рассмотрим, например, java.lang.String. У него большой публичный API, но что бы вы ни делали с публичным API, оно не может мешать другим пользователям java.lang.String. Совершенно безопасно использовать в качестве зависимости от нескольких мест одновременно. С другой стороны, весь ад развалился бы, если бы вы позволили пользователям java.lang.String иметь прямой доступ к внутреннему массиву символов (что позволило бы на месте изменять неизменяемые строки .... неприятно !!).
P.S. Похвальное упоминание идет в OSGi, потому что это довольно удивительная технология и очень полезная во многих обстоятельствах. Тем не менее, его приятное место действительно связано с развертыванием и управлением жизненным циклом модулей (остановка / запуск / загрузка и т. Д.). Вам не нужно это для организации кода ИМХО.