Gradle api 与多模块项目中实施的最佳实践

Best practice for Gradle api vs implementation in multi-module project

这不是关于 apiimplementation 之间区别的常见问题,希望从构建多模块项目的角度来看会更先进、更有趣。

假设我在应用程序中有以下模块

现在模块之间的关系是:

base 换行 library

feature1feature2 使用(取决于)base

app 放在一起 feature1feature2

这个多模块结构中的所有内容都应该能够使用 Gradle 的 implementation 依赖项工作,并且不需要在任何地方使用 api 子句。

现在,假设 feature1 需要访问 library 中包含的 base 的实现细节。

为了让 libraryfeature1 可用,据我所知,我们有两个选择:

  1. base 中的 api 更改 implementation 以将依赖性泄露给依赖于 base

    [=128 的模块=]
  2. library 作为 implementation 依赖添加到 feature1 而不会 base 泄漏对 library[=51= 的依赖]

当然,为了问题的缘故,这个例子已经被简化了,但是你明白这如何成为一个配置地狱,其中有大量模块,具有 4 或 5 级依赖关系。

我们可以创建一个 base-feature 中间模块,它可以包装 base 并为 feature1 提供另一个抽象级别以在不泄漏 library 的情况下使用,但让我们保留它超出此问题范围的解决方案将重点放在依赖项的设置上。


我在上述选项中发现的一些权衡

选项 1) 优点

选项 1) 缺点

选项 2) 优点

选项 2) 缺点


现在问题

感谢您的宝贵时间。

Gradle forum 主题重新发布。

您描述的是关于分层架构系统的相当普遍的讨论,也称为 "strict" 与 "loose" 分层,或 "open" 与 "closed" 层。请参阅 Software Architecture Patterns 中的这一章(希望对您也免费)了解一些符号学,这不太可能对您的选择有很大帮助

从我的角度来看,如果一个模块需要打破分层,我会为项目结构建模,以最直接和可见的方式公开它。在这种情况下,它意味着添加 library 作为 feature1 的实现依赖。是的,它使图表更丑陋,是的,它迫使您在升级时接触更多文件,这就是重点 - 您的设计存在缺陷,现在可见。

如果很少有模块需要以相同的方式打破层封装,我可能会考虑添加一个单独的基础模块来公开该功能,名称如 base-xyz。添加一个新模块是一件大事,不是因为技术工作,而是因为我们的大脑一次只能处理这么多 "things"(分块)。我相信 Gradle "variants" 可用时也是如此,但我还不能断言,因为我还没有亲自尝试过它们。

如果 base 模块的所有客户端都需要访问 library(即因为您在 public 签名中使用 类 或来自 library 的例外) 那么您应该将 library 公开为 base 的 API 依赖项。缺点是 library 成为 base 的 public API 的一部分,并且它可能比您想要的要大,并且不受您的控制。 Public API 是您负责的事情,您希望它保持小巧、有据可查且向后兼容。

在这一点上,您可能正在考虑拼图模块(很好)、osgi(错误...不要),或者包装您需要在自己的 类 中公开的部分库(也许吧?)

仅仅为了打破依赖而包装并不总是一个好主意。一方面,它增加了您维护和(希望)记录的代码量。如果你开始在 base 层做一些小的改编,而 library 是一个众所周知的库,你就会引入(增值)不一致——人们需要时刻警惕他们对 lib 的假设是否仍然成立.最后,瘦包装器通常最终会泄漏库设计,因此即使它们包装了 API - 这仍然会迫使您在 replace/upgrade lib 时接触客户端代码,此时您可能已经最好直接使用 lib。

因此,如您所见,是关于权衡取舍和可用性。 CPU 不关心你的模块边界在哪里,所有的开发人员都是不同的——有些人能更好地处理大量简单的事情,有些人能更好地处理少量高度抽象的概念。

当任何好的设计都可行时,不要沉迷于最好的(如 Bob 叔叔会做什么)设计。为了引入秩序而证明的额外复杂性是一个模糊的数量,并且是你负责决定的。给你最好的电话,不要害怕明天改变它:-)