简体   繁体   English

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

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

This is not the commonly asked question about the difference between api and implementation , and hopefully will be more advance and interesting from the point of view of architecting a multi-module project. 这不是关于apiimplementation之间差异的常见问题,并且从架构多模块项目的角度来看,希望会更加先进和有趣。

Let say I have the following modules in an application 假设我在应用程序中有以下模块

  • library
  • base
  • feature1
  • feature2
  • app

Now the relations between the modules are: 现在模块之间的关系是:

base wraps library base包装library

feature1 and feature2 make use (depends) on base feature1feature2base上使用(取决于)

app puts together feature1 and feature2 appfeature1feature2放在一起

Everything in this multi module structure should be able to work using Gradle's implementation dependencies and there's no need to use the api clause anywhere. 这个多模块结构中的所有内容都应该能够使用Gradle的implementation依赖项工作,并且不需要在任何地方使用api子句。

Now, let say feature1 needs to access an implementation detail of base included in library . 现在,假设feature1需要访问base包含的library的实现细节。

In order to make library available to feature1 we have two options as far as I can tell: 为了使library可用于feature1 ,据我feature1我们有两个选项:

  1. Change implementation for api in base to leak the dependency to modules that depend on base 更改implementationapibase泄漏依赖于依赖于模块base

  2. Add library as an implementation dependency to feature1 without having base leak the dependency on library library作为implementation依赖项添加到feature1而没有base泄漏对library的依赖性

Of course the example has been simplified for the sake of the question, but you understand how this can became a configuration hell with a big number of modules with 4 or 5 levels of dependencies. 当然,为了解决这个问题,这个例子已经被简化了,但是你明白了这可能会成为一个具有4或5级依赖关系的大量模块的配置地狱。

We could create a base-feature intermediate module that can wrap base and provide another level of abstraction for feature1 to consume without leaking library , but let's leave that solution out of the scope of this problem to focus on the setup of the dependencies. 我们可以创建一个base-feature中间模块,它可以包装base并为feature1提供另一个抽象级别,而不会泄漏library ,但让我们将该解决方案排除在此问题的范围之外,以专注于依赖项的设置。


Some trade-offs that I detected on the above options: 我在上述选项中检测到的一些权衡

Option 1) pros 选项1)专业人士

  • Smaller build.gradle 's files, as no need to repeat implementation clauses 较小的build.gradle文件,因为不需要重复implementation子句
  • Faster build scrips edits. 更快的构建编程编辑。 Just make the single change on the api clause and see the changes propagated to all consumer modules 只需对api子句进行单一更改,并查看传播到所有使用者模块的更改

Option 1) cons 选项1)缺点

  • Classes might come up available in modules that shouldn't have access to them. 类可能会出现在无法访问它们的模块中。
  • Prone to miss use by developers, as they have implementations available and not only the module interfaces. 很容易被开发人员使用,因为他们有可用的实现,而不仅仅是模块接口。

Option 2) pros 选项2)专业人士

  • It makes crystal clear which dependencies the module has. 它清楚地表明模块具有哪些依赖关系。
  • No guessing where the classes are coming from (think 4 or 5 levels of modules leaking dependencies), as their origin is always declared in the module dependencies. 不要猜测类的来源(认为4或5级模块泄漏依赖关系),因为它们的起源总是在模块依赖项中声明。

Option 2) cons 选项2)缺点

  • Makes updating a dependency more tedious, as all the modules with the implementation clause have to be modified. 使更新依赖项更加繁琐,因为必须修改具有implementation子句的所有模块。 Even though I believe this is a good thing because it keeps track exactly of how a change modified the project, I see how it can take more time. 即使我认为这是一件好事,因为它完全跟踪变更如何修改项目,我看到它可能需要更多时间。

Now the questions : 现在的问题是

  • Is there any trade-offs in terms of compilation of this multi-module scenario? 在编译这个多模块场景时是否有任何权衡取舍?

  • Is a module leaking a dependency "faster" to be compiled for consumer modules? 是否一个模块泄漏依赖“更快”以便为消费者模块编译?

  • Does it make a substantial difference in build times? 它是否会在构建时间上产生重大影响?

  • What other side effects, pros/cons am I missing ? 我还缺少哪些其他副作用,利弊?

Thanks for your time. 谢谢你的时间。

Reposting from the Gradle forum thread. Gradle论坛帖子中重新发布。

What you describe is a fairly common discussion about layered architecture systems, also known as "strict" vs "loose" layering, or "open" vs "closed" layers. 您所描述的是关于分层架构系统的相当常见的讨论,也称为“严格”与“松散”分层,或“开放”与“封闭”层。 See this (hopefully free for you too) chapter from Software Architecture Patterns for some semiotics which is unlikely to help you much with your choice 软件架构模式中可以看到这个(希望对你来说也是免费的)一些符号学的章节,它不太可能对你的选择有所帮助

From my point of view, if a module needs to break layering, I'd model the project structure to expose this in the most direct and visible way. 从我的角度来看,如果一个模块需要打破分层,我会对项目结构进行建模,以最直接和最明显的方式公开它。 In this case it means adding library as implementation dependency of feature1 . 在这种情况下,它意味着添加library作为feature1实现依赖性。 Yes it makes the diagram uglier, yes it forces you to touch few more files on upgrade, and that is the point - your design has a flaw and it is now visible. 是的,这使得图表更加丑陋,是的,它迫使您在升级时触摸更多文件,这就是重点 - 您的设计存在缺陷,现在可见。

If few modules need to break the layer encapsulation in the same way, I may consider adding a separate base module exposing that functionality, with a name such as base-xyz . 如果很少有模块需要以相同的方式打破层封装,我可以考虑添加一个单独的基本模块,使用诸如base-xyz类的名称来公开该功能。 Adding a new module is a big thing, not because of the technical work, but because our brain can handle only so many "things" at a time (chunking). 添加新模块是一件大事,不是因为技术工作,而是因为我们的大脑一次只能处理这么多“事物”(分块)。 I believe the same would hold for Gradle "variants" when they become available, but I can't claim that yet as I haven't tried them hands on. 我相信当Gradle“变种”可用时,它们会同样适用,但我还没有声称,因为我没有尝试过它们。

If all clients of the base module need to access library (ie because you use classes or exceptions from library in your public signatures) then you should expose library as API dependency of base . 如果所有的客户base模块需要访问library (即因为你使用类或例外,从library在公开的签名),那么你应该揭露library作为API的依赖base The downside of that is that library becomes part of the public API of base , and it is probably bigger than you would like, and not under your control. 其缺点是library成为base公共API的一部分,它可能比您想要的更大,而且不在您的控制之下。 Public API is something you are responsible for, and you want to keep it small, documented, and backwards compatible. 公共API是您负责的事情,并且您希望将其保持小,记录和向后兼容。

At this point you may be thinking about jigsaw modules (good), osgi (err... don't), or wrapping the parts of lib that you need to expose in your own classes (maybe?) 此时你可能正在考虑拼图模块(好),osgi(错误...不要),或者包装你需要在自己的类中暴露的lib部分(也许?)

Wrapping only for the sake of breaking dependencies is not always a great idea. 仅仅为了破坏依赖性而包装并不总是一个好主意。 For one it increases the amount of code you maintain and (hopefully) document. 例如,它增加了您维护和(希望)文档的代码量。 If you start doing small adaptations in the base layer, and the library is a well known library, you introduce (value added) inconsistencies - one needs to always be on guard whether their assumptions for lib still hold. 如果您开始在base层中进行小的修改,并且library是一个众所周知的库,那么您会引入(增值)不一致性 - 需要始终保持他们对lib的假设是否仍然成立。 Finally, often the thin wrappers end up leaking the library design, so even if they wrap the API - that still forces you to touch the client code when you replace/upgrade lib, at which point you may have been better off using lib directly. 最后,瘦包装器通常会泄漏库设计,所以即使它们包装了API - 当你更换/升级lib时仍然会强迫你触摸客户端代码,此时你可能最好直接使用lib。

So, as you can see, is about trade-offs and usability. 因此,正如您所看到的,是关于权衡和可用性。 The CPU doesn't care where your module boundaries lie, and all developers are different - some cope better with large amount of simple things, some cope better with small number of highly abstract concepts. CPU并不关心模块边界所在的位置,并且所有开发人员都是不同的 - 有些人会更好地处理大量简单的事情,有些人会更好地处理少量高度抽象的概念。

Don't obsess about the best (as in What Would Uncle Bob Do) design when any good design would work. 当任何好的设计都能奏效时,不要痴迷于最好的(就像在鲍勃叔叔那里做的那样)。 The amount of extra complexity that is justified for the sake of introducing order is a fuzzy quantity, and is something that you are in charge of deciding. 为了引入顺序而合理的额外复杂性的数量是模糊量,并且是您负责决定的事情。 Make you best call and don't be afraid to change it tomorrow :-) 让你最好的电话,不要害怕明天改变它:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Intellij IDEA 13.1.13中具有Android + Gradle的多模块项目 - Multi-module project in Intellij IDEA 13.1.13 with Android+Gradle 如何在Gradle多模块项目中使用ObjectBox? - How to use ObjectBox in Gradle multi-module project? 使用 gradle DSL 在多模块项目中声明风味依赖项 - Declaring flavor dependencies in multi-module project using gradle DSL 多模块gradle项目 - 执行所有单元测试 - Multi-module gradle project - Executing all Unit tests 具有多模块android库的Gradle配置 - Gradle configuration with multi-module android library Gradle多模块依赖问题 - Gradle multi-module dependecy issue 更新到android gradle插件2.3后,多模块MultiDex项目不会运行测试 - Multi-module MultiDex project won't run tests after update to android gradle plugin 2.3 多模块项目中缺少顶级 build.gradle - Missing top-level build.gradle in multi-module project 使用Gradle为多模块项目设置SonarQube 5.4:无法通过ClassLoader访问类XYZ - Set up SonarQube 5.4 for multi-module project with Gradle: Class XYZ is not accessible through the ClassLoader JaCoCo 与 Gradle Kotlin 多模块 Android 项目 - isTestCoverageEnabled 实际上是做什么的? - JaCoCo with Gradle Kotlin multi-module Android project - what does isTestCoverageEnabled actually do?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM