这不是关于 api 和 implementation 之间区别的常见问题,希望从构建多应用程序的角度来看会更先进、更有趣模块项目。
假设我在应用程序中有以下模块
库基础feature1feature2应用现在模块之间的关系是:
base 包装library
feature1 和 feature2 使用(依赖于)base
app 将 feature1 和 feature2 放在一起
这个多模块结构中的一切都应该能够使用 Gradle 的 implementation 依赖项工作,并且不需要在任何地方使用 api 子句。
现在,假设 feature1 需要访问包含在 library 中的 base 的实现细节。
为了使 library 对 feature1 可用,据我所知,我们有两个选择:
更改 base 中 api 的 implementation 以将依赖性泄漏到依赖于 base 的模块
将 library 添加为 implementation 对 feature1 的依赖,而 base 不会泄漏对 库
当然,为了问题的缘故,这个例子已经被简化了,但是你明白这会如何变成一个配置 hell ,其中有大量模块和 4 或 5 级依赖关系。
我们可以创建一个 base-feature 中间模块,它可以包装 base 并为 feature1 提供另一个抽象级别以在不泄漏的情况下使用 library,但让我们将该解决方案置于此问题的范围之外,以专注于依赖项的设置。
我在上述选项中发现的一些权衡:
选项 1) 优点
build.gradle 文件,因为不需要重复 implementation 子句api 子句进行单一更改,然后查看传播到所有消费者模块的更改选项 1) 缺点
选项 2) 优点
选项 2) 缺点
implementation 子句的模块。尽管我认为这是一件好事,因为它可以准确跟踪变更对项目的影响,但我知道这会花费更多时间。现在问题:
在这个多模块场景的编译方面是否有任何权衡?
模块泄漏依赖项是否“更快”以供消费者模块编译?
它会显着缩短构建时间吗?
我还遗漏了哪些其他副作用、优点/缺点?
感谢您的宝贵时间。
最佳答案
转自Gradle forum线程。
您描述的是关于分层架构系统的相当普遍的讨论,也称为“严格”与“松散”分层,或“开放”与“封闭”层。请参阅 Software Architecture Patterns 中的这一章(希望对您也免费)对于一些不太可能帮助你做出选择的符号学
从我的角度来看,如果一个模块需要打破分层,我会为项目结构建模,以最直接和可见的方式公开它。在这种情况下,这意味着添加 library 作为 feature1 的实现依赖项。是的,它使图表更丑陋,是的,它迫使您在升级时接触更多文件,这就是重点 - 您的设计存在缺陷,现在可以看到。
如果很少有模块需要以相同的方式打破层封装,我可能会考虑添加一个单独的基础模块来公开该功能,名称如 base-xyz。添加一个新模块是一件大事,不是因为技术工作,而是因为我们的大脑一次只能处理这么多“东西”(分块)。我相信当 Gradle“变体”可用时,这同样适用于它们,但我还不能断言,因为我还没有亲自尝试过它们。
如果 base 模块的所有客户端都需要访问 library(即因为您在公共(public)签名中使用了来自 library 的类或异常)那么你应该将 library 公开为 base 的 API 依赖项。缺点是 library 成为 base 的公共(public) API 的一部分,它可能比您想要的要大,并且不受您的控制。公共(public) API 是您负责的东西,您希望它保持小巧、有文档记录和向后兼容。
在这一点上,您可能正在考虑拼图模块(好)、osgi(错误...不要),或者包装您需要在自己的类中公开的部分库(也许?)
仅仅为了打破依赖而包装并不总是一个好主意。一方面,它增加了您维护和(希望)记录的代码量。如果你开始在 base 层做一些小的改编,而 library 是一个众所周知的库,你就会引入(增值)不一致——人们需要时刻警惕是否他们对 lib 的假设仍然成立。最后,瘦包装器通常最终会泄露库设计,因此即使它们包装了 API - 这仍然会迫使您在替换/升级 lib 时接触客户端代码,此时您最好直接使用 lib。
因此,如您所见,这是关于权衡取舍和可用性的问题。 CPU 不关心你的模块边界在哪里,所有开发人员都是不同的 - 有些人可以更好地处理大量简单的事情,有些人可以更好地处理少量高度抽象的概念。
当任何好的设计都可行时,不要执着于最好的设计(如 Bob 叔叔会做什么)。为了引入秩序而证明的额外复杂性是一个模糊的数量,并且是你负责决定的。让你最好的电话,不要害怕明天改变它:-)
关于android - Gradle api 与多模块项目中实现的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54554251/
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我怎样才能完成http://php.net/manual/en/function.call-user-func-array.php在ruby中?所以我可以这样做:classAppdeffoo(a,b)putsa+benddefbarargs=[1,2]App.send(:foo,args)#doesn'tworkApp.send(:foo,args[0],args[1])#doeswork,butdoesnotscaleendend 最佳答案 尝试分解数组App.send(:foo,*args)
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or