Java 如何解决缺少包访问说明符的问题?

Java 如何解决缺少包访问说明符的问题?,java,jar,packages,encapsulation,class-visibility,Java,Jar,Packages,Encapsulation,Class Visibility,我是Java新手。我发现,在尝试构建代码结构时,Java将源文件组织目录结构与包结构紧密地联系在一起,包结构与类的外部可见性紧密地联系在一起。一个类要么对所有其他包可见,要么不可见 这使得在保持良好封装的同时,很难将公共库的内部实现细节组织到相关功能的逻辑单元中。最好的解释是: 如今,一个实现可以划分为多个包。 这种实现的子部分需要更紧密地耦合到 与周围的软件环境无关。今天 设计人员被迫声明程序中 公共实施的其他子部分所需——因此 使它们全球可访问,这显然是次优的 或者,可以将整个实现放在单个

我是Java新手。我发现,在尝试构建代码结构时,Java将源文件组织目录结构与包结构紧密地联系在一起,包结构与类的外部可见性紧密地联系在一起。一个类要么对所有其他包可见,要么不可见

这使得在保持良好封装的同时,很难将公共库的内部实现细节组织到相关功能的逻辑单元中。最好的解释是:

如今,一个实现可以划分为多个包。 这种实现的子部分需要更紧密地耦合到 与周围的软件环境无关。今天 设计人员被迫声明程序中 公共实施的其他子部分所需——因此 使它们全球可访问,这显然是次优的

或者,可以将整个实现放在单个 包裹这解决了上面的问题,但是很难处理,并且暴露了 所有子部件的所有内部部件相互连接

所以我的问题是,对于这个限制有什么解决办法,有什么利弊?JSR中提到了两个用于违反封装的逻辑分组的包;把所有的东西都放在一个单一的包裹里,太笨重了。这些变通方法还有其他优点/缺点吗?还有其他解决办法吗?我已经模糊地意识到OSGi捆绑包,但我发现很难理解它们是如何工作的,它们的优缺点可能是什么,这可能是一个缺点。与普通软件包相比,它似乎对开发和部署非常具有侵入性

注:我会投票选出任何好的答案,但最好的答案是综合考虑他人剽窃的利弊的答案

相关但不重复!问题 期待着“可能重复”的呼声,这里有一些类似的问题,我已经发现了;我把它们放在这里作为参考,并解释它们为什么不回答我的问题

:询问如何执行此操作,但鉴于在当前的Java版本中不可能执行此操作,因此不讨论解决方法。有一些有趣的指向Java8的指针。 -上述问题基本上是重复的。 -问题和答案似乎是特定于OSGi或Eclipse插件的。
像这样的工具可以用来重新打包JAR,只公开配置文件中指定的类。除了优化、内联和模糊处理之外,它还实现了这一点。您可能可以在Maven或Ant构建中设置ProGuard,因此您可以将库公开方法编写为public,然后使用ProGuard将它们从生成的JAR中删除。

我会着手的。窃取此答案并添加/更正/请详细说明

对多个逻辑分组使用多个包 优点:相关代码的有效逻辑分组

缺点:当不同包中的内部实现细节类需要相互使用时,必须将它们公开,甚至对违反封装的最终用户也是如此。通过对包含内部实现细节(如.internal或.impl)的包使用标准命名约定来解决此问题

把所有东西放在一个包裹里 优点:有效的封装

缺点:如果库包含许多类,那么它的开发/维护将非常困难

使用OSGi捆绑包 赞成者:?他们解决问题了吗

缺点:与仅部署.jar文件相比,对于库用户、作者和部署来说,在开发过程中似乎非常麻烦

等待Java8中的拼图 优点:永久解决问题


缺点:还不存在,还不知道具体的发布日期。

我从未发现这是个问题。如果您想调用它,那么解决方法称为良好的API设计

如果您的库设计得很好,那么您几乎可以始终执行以下操作:

将主公共API放在一个包中,例如my.package.core或仅my.package 根据逻辑分组将帮助器模块放在其他包中,但为每个模块提供自己的公共API子集,例如my.package.foobarimpl.FoobarFactory之类的工厂类 主公共API包仅使用辅助模块的公共API 您的测试也应该主要针对公共API运行,因为这是您关心的回归或功能 因此,对我来说,包的正确封装级别是公开足够的公共API,使包可以有效地用作依赖项。不多也不少。无论它是由同一库中的另一个包使用还是由外部用户使用,都不应该有任何关系。如果你围绕这个原则设计你的软件包,你就增加了有效重用的机会

只要您的API设计合理,使包的各个部分可以在全球范围内访问实际上不会造成任何伤害。请记住,包不是对象实例,因此是encap 隔离并不重要:公开包的元素通常比公开类的内部实现细节危害小得多,我同意类的内部实现细节几乎应该是私有的/受保护的

以java.lang.String为例。它有一个很大的公共API,但是无论您使用公共API做什么,都不能干扰java.lang.String的其他用户。同时从多个地方将其用作依赖项是完全安全的。另一方面,如果您允许java.lang.String的用户直接访问内部字符数组,这将允许不可变字符串的就地变异,那么所有的麻烦都会爆发。。。。讨厌


值得一提的是OSGi,因为它是一项非常棒的技术,在许多情况下都非常有用。然而,它的最佳点实际上是关于模块停止/启动/加载等的部署和生命周期管理。。对于代码组织IMHO,您并不真正需要它。

IMO,如果您有两个模块希望特别访问彼此的非公共成员,事实上,你并没有很好的封装。我认为当你只考虑一个模块需要一个其他模块的访问时,这个问题仍然存在。我同意相互依赖很可能是一个指示器,你应该重构出一个第三,共享的模块。对不起,我不清楚。我的意思是,真正封装的模块应该只需要访问其他模块的公共成员。可能我不清楚:我同意,这完全是这个问题的重点——Java没有模块,它有包,但代码结构与可访问性密切相关——也就是说,如果我的“模块”如API,库应在逻辑上分解为多个包,例如面向公共的部分、私有实现细节、,Java迫使我公开impl详细信息——只是为了让我的库的公共部分可以看到它——这反过来意味着所有客户端都可以看到实现的详细信息。这听起来好像对库开发人员来说具有“使用多个包”的优势,然后有效地修改它,使其具有将所有内容放在一个包中的封装优势——我试图找到一些文档,它们说它实际上可以做到这一点:你知道你是否可以说服它做到这一点,而不必做规范中所有的模糊处理razzmatazz?put-dontobfuscate?我不同意。如果my.package.foobarimpl.FoobarFactory是一个内部实现细节,但客户端可能最终依赖它,因此我无法在不中断可能已开始使用它的客户端的情况下更改该实现细节。无论它是否被同一个库impl detail使用,都绝对重要——应该允许在不破坏客户端或外部用户的情况下进行更改。impl细节的全部要点是,我可以在不破坏客户机的情况下自由更改它。@bacar-如果真是这样,那么它是一个实现细节,那么它在逻辑上属于同一个包。这就是IMHO的软件包——一个具有公共API并隐藏其实现细节的逻辑功能包。谢谢——这就是问题所在:您的语句非常严格,不允许在软件包中进行进一步的逻辑分组/细分。考虑一个API,其内部实现细节需要一些类与特定的DB进行交互;多处理一些运输,其他处理配置等;如果我想在逻辑上将这些包分组到.db、.transport、.config包中,那么每个impl detail包中至少有一个类必须公开。或者,我不得不把所有东西都放在一个包裹里——笨重。