Gradle api与在多模块项目中实现的最佳做法

时间:2019-02-06 13:00:21

标签: android gradle android-gradle multi-module

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

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

  • library
  • base
  • feature1
  • feature2
  • app

现在模块之间的关系是:

base包装library

feature1feature2base上使用(取决于)

appfeature1feature2

放在一起

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

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

为了使library可以使用feature1,据我所知,我们有两个选择:

  1. implementation中将api的{​​{1}}更改为将依赖项泄漏给依赖于base的模块

  2. base作为library的依存关系添加到implementation,而不会使feature1泄漏对base的依存关系

当然,出于这个问题,示例已被简化,但是您了解了如何将其变成具有大量具有4或5个依赖级别的模块的配置地狱。

我们可以创建一个library中间模块,该模块可以包装base-feature并为base提供另一级别的抽象以供使用,而不会泄漏feature1,但是让我们省略该解决方案问题的范围集中在依赖项的设置上。


我在上述选项中检测到的

一些权衡

选项1)优点

  • 较小的library的文件,因为无需重复build.gradle子句
  • 更快的构建脚本编辑。只需对implementation子句进行一次更改,即可看到更改已传播到所有使用者模块

选项1)缺点

  • 类可能出现在不应访问它们的模块中。
  • 开发人员可能会错过使用Prone的机会,因为他们拥有可用的实现方式,而不仅仅是模块接口。

选项2)专业人士

  • 它使模块具有哪些依存关系清晰可见。
  • 不要猜测这些类的来源(想想4或5级模块泄漏依赖项),因为它们的起源总是在模块依赖项中声明的。

选项2)缺点

  • 由于必须修改所有带有api子句的模块,因此更新依赖项变得更加乏味。尽管我认为这是一件好事,因为它可以准确跟踪更改是如何修改项目的,但我知道它可能需要更多时间。

现在是问题

  • 在此多模块方案的编译方面是否有权衡取舍?

  • 模块是否泄漏了“更快”的依赖关系,以便为使用者模块进行编译?

  • 它在构建时间上有实质性的不同吗?

  • 我还缺少其他哪些副作用?

感谢您的时间。

1 个答案:

答案 0 :(得分:2)

Gradle forum线程重新发布。

您所描述的是关于分层体系结构系统的相当普遍的讨论,也称为“严格”层与“松散”层,或“开放”层与“封闭”层。参见Software Architecture Patterns的本章(希望对您也免费)中的一些符号符号,这些符号符号不太可能对您的选择有很大帮助

从我的角度来看,如果模块需要中断分层,则可以对项目结构进行建模,以最直接,最直观的方式展示它。在这种情况下,这意味着添加output作为[[3, 4, 2, 2, 1], [4, 5, 2, 2, 1], [3, 4, 3, 3, 2], [2, 2, 2, 2, 2], [1, 1, 2, 2, 2]] 的实现依赖项。是的,它使图表更丑陋,是的,它迫使您在升级时碰几个文件,这就是要点-您的设计有缺陷,现在可以看到。

如果很少有模块需要以相同的方式破坏层封装,我可以考虑添加一个单独的基本模块来公开该功能,其名称为library。添加新模块是一件大事,不是因为技术上的工作,而是因为我们的大脑一次只能处理这么多的“事物”(块)。我相信,当Gradle“变体”可用时,也是如此,但是我还不能断言,因为我还没有尝试过。

如果feature1模块的所有客户端都需要访问base-xyz(即因为您在公共签名中使用了base的类或异常),则应该公开library作为library的API依赖项。缺点是library成为base的公共API的一部分,它可能比您想要的更大,并且不受您的控制。公共API是您要负责的事情,您想使其保持小巧,有文档记录并向后兼容。

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

仅仅为了打破依赖关系而包装并不是一个好主意。一方面,它增加了您维护(以及希望)文档的代码量。如果您开始在library层进行小的修改,并且base是一个众所周知的库,则会引入(增值)不一致性-必须始终警惕其对lib的假设是否仍然成立。最后,薄包装器通常最终会泄漏库设计,因此,即使它们包装了API-在替换/升级lib时仍然会迫使您触摸客户端代码,这时最好直接使用lib。

因此,正如您所看到的,是权衡和可用性。 CPU不在乎模块边界在哪里,并且所有开发人员都不相同-有些可以更好地应对大量简单的事情,有些可以更好地应对少数高度抽象的概念。

在任何好的设计都可行的情况下,不要着迷于最好的设计(就像鲍勃叔叔会做的那样)。为引入顺序而辩解的额外复杂性的数量是一个模糊的数量,您可以自行决定。打给您最好的电话,不要害怕明天更改它:-)