在上一篇文章中,介绍了结构性模块化与敏捷之间的关系,在这个系列的第二篇文章中,我们将会研讨OSGi,在实现Java的结构性模块化方面,OSGi扮演了核心的角色;OSGi与流行的敏捷方法论之间存在着自然的联系。
1 但我们已经实现了模块化!
绝大多数开发人员都同意程序应该模块化。尽管在面向对象的程序设计出现的早期,逻辑性模块化的要求就被迅速满足了(见http://en.wikipedia.org/wiki/Design_Patterns), 但是软件行业花费了很长的时间才理解结构性模块化的重要性。特别是,结构性模块化可以提高程序的可维护性和灵活性,控制和减少环境带来的复杂性。
只是JAR文件的组合
在《Java Application Architecture》(本书已经由机械工业出版社引进出版,中文书名为《Java应用架构设计:模块化模式与OSGi》——译者注)一书中,Kirk Knoernschild探索研究了结构性的模块化,并建立了一套关于结构化设计的最佳设计模式。Knoernschild认为,在模块化风潮中并不需要开发模块化的框架;对于Java,JAR文件结构就足够了。
事实上,在“敏捷”开发团队中根据代码库的增长将应用分解为较小的JAR文件的做法并不罕见。随着JAR文件大小的增加,他们会被分解为更小的JAR文件的集合。从代码角度,特别是如果遵循Knoernschild的结构化设计模式,我们会认为,从某个结构层看,程序是模块化的。
但这是否敏捷?
从创建应用的团队,以及负责随后维护人员的角度来看,应用是更敏捷的。团队了解依赖关系,以及变化的影响。然而,这种知识并没有关联到组件上。一旦团队成员离开了公司,程序和业务就有可能立刻受到影响。同样,对第三方(即使是同一个组织的不同团队)来说,该应用可能依然是单独的巨大代码库。
如果程序中只有一层结构性的模块,它必然不是自描述的。由于缺乏描述模块之间内在关系的元数据,由此产生的业务系统本质上是脆弱的。
那么MAVEN呢?
Maven文档(项目对象模型,Project Object Model——POM)也能表达组件之间的依赖关系。这些依赖关系由组件的名称定义。
鉴于此,基于Maven的模块化程序可以被任何第三方很容易地整合起来。然而,在上一篇文章我们已经提到,由名称定义依赖关系是有缺陷的。由于组件之间的依赖关系并不能明确表明需求(Requirement)和功能(Capability),第三方不能推断出依赖关系的存在原因和可替代的部分。
程序可以被整合,但却不能变更。这种方式是否使得程序比之前的“JAR文件组合”更为“敏捷”还值得商榷。
对OSGi的需求
就如Knoernschild在《Java应用架构设计》中阐述的那样,一旦实现了结构性模块化,我们很容易就能将它迁移到OSGi 之中,也就是Java领域中的模块化标准。
OSGi不仅帮助我们保证结构性的模块化,它同时提供了必须的元数据来保证我们建立的模块化结构也是敏捷的结构。
OSGi通过需求和功能表达依赖关系。因此,第三方可以立刻知道哪些组件可能是可替换的。因为OSGi同样使用语义化的版本控制,所以第三方可以立即推测出,某个组件的变更是否有破坏系统的潜在危险。
OSGi同样也具有展现结构化层次的能力。
在模块化的一端,我们使用面向服务的架构(Service Oriented Architecture,SOA);在另一端,我们使用Java包和类。然而,正如Knoernschild表述的那样,在这两端之间缺少了必要的层次。
图1: 结构化层次:缺失了中间的部分(Kirk Knoernschild – 2012)