1

我是 Java 新手。我在尝试构建我的代码时发现,Java 将源文件组织(目录结构)与包结构和包结构与类的外部可见性密切相关(一个类对所有其他包可见,或者不可见)。

这使得在保持良好封装的同时将我的公共库的内部实现细节组织成相关功能的逻辑单元变得相当困难。JSR 294解释得最好:

今天,一个实现可以分成多个包。这种实现的子部分需要更紧密地相互耦合,而不是与周围的软件环境耦合。今天,设计人员被迫将实现的其他子部分所需的程序元素声明为公开的——从而使它们在全球范围内可访问,这显然是次优的。

或者,整个实现可以放在一个单独的包中。这解决了上面的问题,但很笨拙,并且将所有子部分的所有内部都暴露给彼此。

所以我的问题是,这种限制存在哪些解决方法,优缺点是什么?JSR 中提到了两个——使用包进行逻辑分组(违反封装);把所有东西都放在一个包里(笨重)。这些变通办法还有其他优点/缺点吗?还有其他解决方案吗?(我已经模糊地意识到 OSGi 包,但我发现很难理解它们是如何工作的以及可能的优点/缺点是什么(也许这是一个缺点)。与香草包相比,它似乎非常具有侵入性,到开发和部署。

注意:我会投票赞成任何好的答案,但最好的答案将是一个综合考虑其他人的利弊的答案(抄袭!)。

相关(但不重复!)问题

期待“可能重复”的呼喊,这是我在 SO 上发现的类似问题;我将它们呈现在这里以供参考,并解释为什么它们不回答我的问题。

4

3 回答 3

0

我从来没有发现这是一个问题。解决方法(如果您想这样称呼它)称为良好的 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 的用户直接访问内部字符数组(这将允许不可变字符串的就地突变......讨厌!!),所有的地狱都会崩溃。

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

于 2012-03-08T01:41:50.970 回答
0

ProGuard之类的工具可用于重新打包 JAR,仅公开您在配置文件中指定的那些类。(除了优化、内联和混淆之外,它还执行此操作。)您可能能够在例如 Maven 或 Ant 构建中设置 ProGuard,因此您可以编写公开方法的库,然后使用 ProGuard 从生成的 JAR。

于 2012-03-08T00:53:59.937 回答
0

我会让球滚动。窃取这个答案并添加到它/更正它/请详细说明!

为多个逻辑分组使用多个包

优点:相关代码的有效逻辑分组。

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

把所有东西放在一个包里

优点:有效的封装

缺点:如果库包含许多类,则对库的开发/维护很不方便

使用 OSGi 包

优点:?(他们解决问题了吗?)

缺点:与仅部署 .jar 文件相比,在开发(对于库用户和作者)和部署方面似乎非常具有侵入性。

等待 Java 8 中的 Jigsaw

http://openjdk.java.net/projects/jigsaw/

优点:永久解决问题?

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

于 2012-03-08T00:59:42.070 回答