我想到的用例是fastutil,这是一个大型库,包含数千个类型特定集合的实现。该库很受欢迎,有近500个Maven项目使用,但许多用户认为其大小(>10K类(无法管理,并要求将jar拆分为更小的部分。
jar目前是OSGi模块化的,如下所示:
Automatic-Module-Name: it.unimi.dsi.fastutil
Bundle-Name: it.unimi.dsi.fastutil
Bundle-SymbolicName: it.unimi.dsi.fastutil
Export-Package: it.unimi.dsi.fastutil.*
Bundle-Version: ${version}
我想把这个jar分成三部分(而且,为了清楚起见,一个贡献者创建了上面的信息——我对OSGi知之甚少(。问题是,拆分必然涉及到拆分包——由于库的性质,无法避免这种情况。我读过很多反对这种做法的文章,但这是我们唯一的选择。我的理解是,在这种情况下,应该使用Require-Bundle
,它允许在捆绑包之间拆分包。
然而,我还没有找到以前存在的捆绑包的这种做法的例子。在这种情况下,我们希望这种修改能够透明地通过用户。
设想在两个罐子中进行拆分,我目前的理解是使用以下模块化:核心罐子将使用
Automatic-Module-Name: it.unimi.dsi.fastutil.core
Bundle-Name: it.unimi.dsi.fastutil.core
Bundle-SymbolicName: it.unimi.dsi.fastutil.core
Export-Package: it.unimi.dsi.fastutil.*
Bundle-Version: ${version}
然后罐子的其余部分将被模块化为
Automatic-Module-Name: it.unimi.dsi.fastutil
Bundle-Name: it.unimi.dsi.fastutil
Bundle-SymbolicName: it.unimi.dsi.fastutil
Require-Bundle: it.unimi.dsi.fastutil.core
Export-Package: it.unimi.dsi.fastutil.*
Bundle-Version: ${version}
这似乎是合理的,但现在包的一半的捆绑包名称与另一半的模块名称不同。这会造成问题吗?
更一般地说,对于上面的用例(已经是OSGi模块化的jar,需要跨包拆分(,最佳实践是什么?
更一般地说,对于上面的用例(已经是OSGi模块化的jar,需要跨包拆分(,最佳实践是什么?
如果您遇到所描述的问题,则没有模块化任何东西。相反地模块化的目的是创建高度内聚的模块,通过定义良好的接口进行交互。您的建议表明,模块化背后的这个想法还没有得到很好的理解。
因为一个大型库有太多的类而将其拆分是模块化中最糟糕的事情。你增加了耦合,降低了内聚力。你做了一些可怕的事情,比如jackson,你的类路径上总是需要6个bundle。
对于模块化系统,封装是一个主要的设计方面,这不是你以后可以开始考虑的事情。如果您没有从模块化中获得巨大的好处,那么您很可能会一直与OSGi作斗争。OSGi是模块化应用程序的最佳环境,但对其他应用程序来说却是个婊子养的。
如果你对模块化很认真,那么你应该尝试以这样一种方式分解包,即每个子束具有更高的内聚性,并且降低了整体耦合。这大大降低了复杂性。
有一些像Tarjan算法这样的技术可以分析代码并找到自然的模块边界。大多数情况下,大混乱是由支持其他可选库引起的,但编码方式使它们成为强制性的传递依赖项。当我需要比较一些陷入困境的公司的课程时,我总是很难过。
话虽如此,但毫无疑问,使用设计良好的服务API通常可以使绝大多数类私有化,尤其是在设计时考虑到配置管理员。
最后,如果您在OSGi方面遇到问题,很可能是因为它告诉您您不是在模块化工作。
正如其他人所说,拆分包是一种糟糕的做法。目前还没有关于如何做到这一点的最佳实践。也就是说,人们确实需要在某个时候把一个杂草丛生的包裹拆开。我想向您介绍一下Eclipse,它已经多次这样做了。看见https://www.eclipse.org/equinox/documents/3.2-migration.php以讨论一个示例:CCD_ 2包。我还将注意到,EclipseIDE并不是使用包导出/导入的最佳实践示例,因为它过度使用了RequireBundle。