Java应用架构设计:模块化模式与 OSGi(2)

 由  张卫滨 发布

第 2 章 模块化的两个方面

模块化包含两个方面:运行时模型和开发模型。现在,关注的重点是 运行时 模型,出 现了一些支持运行时模块化的框架。但最终,随着运行时 模型的采用,开发模型的重要性将会占据主导地位。在本书中,关注开发模型的模式称为设计范式(design paradigm)。

2.1 运行时模型

运行时模型关注如何在运行时对模块化的软件系统进行管理。在Java平台中,模块化系统的事实标准是OSGi,很多应用平台借助 OSGi 运行时的能力增强模块化。在一定程度上,这使企业认识到 OSGi 的长处,不过他们所了解的并不够。随着厂商将 OSGi 添加到他们的产品中,很多组织会认识到这能带来两个好处,也就是更快地应用启动时间和平台适应性。(关于运行时的益处,参见 13.2 节)

但直到现在,众多广泛使用的平台在内部封装了 OSGi 却选择对企业级开发人员隐藏这一点。所以,开发人员无法构建充分利用模块化运行环境的应用程序。但是,这正在发生着变化,平台开始暴露 OSGi 的长处并允许开发人员利用其强大的运行时能力。我们不再受制于类路径地狱(classpath hell),庞大的应用程序也不会再折磨我们。相反,模块会在运行时发现其他模块,隔离应用程序的人工壁垒也不复存在。

最终,随着对模块化的支持迁移到平台中,企业级开发人员将使用这些框架和技术开发更加模块化的软件系统。当这种情况出现时,开发模型就会变得很重要了。

2.2 开发模型

开发模型解决的问题是开发人员怎样使用框架构建软件应用。开发模型又可以进一步分为两类:编程模型(programming model )和设计范式。在帮助开发人员构建更加模块化的软件应用中,两者都是很重要的。

2.2.1 编程模型

要利用运行时模块系统就需要程序员能够与模块系统的应用程序编程接口(Application Programming Interface,API)交互。但是,依赖这些 API 将会导致重量级(heavyweight)的模块(关于重量级,参见 5.2.2 节),它们很难测试并且很难在运行时系统外执行。为了减少对模块系统 API 的依赖,框架和一些技术必须提供一定程度的抽象,这样代码就不会直接依赖框架的 API 了。这些框架和技术的示例包括 OSGi Blueprint 服务、声明式服务(Declarative Service)、Spring Dynamic Module 以及 iPojo。

借助这些框架和工具,开发人员可以使用运行时模块系统的能力而不用再担心编程模型。这些框架封装了对 API 的依赖,这样代码就不用直接与这些 API 交互了。通过这些框架实现关注点分离能够让 Java 类依旧是简单旧式 Java 对象(Plain Old Java Object,POJO),它们不会依赖模块系统的框架。这使得编码和测试简单了许多。

2.2.2 设计范式

除了运行时模型和编程模型以外,在开发模块化软件时还会有其他的挑战。设计范式也是必须要解决的。一个组织如何创建更加模块化的架构?模块的合适粒度是什么样的?模块间应该存在什么程度的依赖?怎样最小化模块依赖?如何将大的模块拆分为一些更小的更加内聚的模块?怎样将已有的庞大软件系统模块化?应该在何时这样做?这些问题和其他问题是重要的架构和设计问题,这都是在企业级软件开发中与模块化相关的问题。本书中的模式就着重于设计范式。

为了理解设计范式的重要性,借助一些其他技术的历史,我们可以学到重要的经验教训。先看两个不同的技术示例,事实已证明它们与模块设计范式有一些相关性:面向对象(Object-Oriented,OO)编程以及企业级JavaBean(Enterprise JavaBean,EJB)。

1.面向对象

在20世纪90年代初期,面向对象范式被视为救世主。开发团队能够通过组合可重用的对象构建系统。面向对象范式承诺会显著缩减软件的上市时间并提供更高质量的软件。但承诺并没有兑现,面向对象范式的益处从来没有完全实现。有几个原因使得开发团队无法实现这些收益。作为可重用的基础,类的粒度太小。开发团队很难正确地把握和使用面向对象的理念。较深层次结构的继承以及包含太多功能的基类会导致设计不佳和脆弱的软件系统。总之,面向对象开发过早地失效了。

面向对象编程语言的运行时功能提供了如多态、动态绑定等特性,开发人员可以很容易地理解编程模型中的很多方面。使用点号调用方法以及定义私有的成员变量都是很简单的理念。但我们花费了很长的时间才理解怎样使用面向对象技术设计好的程序。换句话说,我们一直与设计范式做斗争。如今被视为面向对象设计的简单事实(“优先使用对象组合而不是对象继承”以及“面向接口编程而不是实现”)在十五年前还是未知的,至少对我们并不是那样明显。即便是现在,我们还在学习新的方法来使用面向对象技术设计更好的软件系统。

2.企业级 JavaBean

企业级 JavaBean,尤其是其中的实体 bean,曾被视为将 Java 开发的业务应用进行组件化的一种方式。EJB 的运行时能力是很有吸引力的——事务、持久化、安全性、透明等——并且它们直接合并到了平台中。不过,有两个很明显的问题:开发模型复杂并且无法很好地理解。

尽管这已经是多年之前的事情了,但我清楚记得第一次接触 EJB 时的情形。我中途参加了一个正在进行开发的团队。那时,团队有超过 100 个的实体 bean,本地开发环境需要四个多小时才能启动而且遍地都是问题。我坚持了三周,然后自愿离开了这个项目。在此之后不久,这个项目最终取消了。EJB 的问题不在于运行时模型或编程模型。有很棒的工具使这些工作变得很容易。运行时模型实现了它的很多承诺,代码生成向导以及众多的工具使得编程模型相对很简单。甚至出现了一些框架使开发人员可以将代码 与 EJB 编程模型解耦,这样他们就可以只设计更简单的 POJO。但是,EJB 是一项新的技术,很多开发人员缺少有效使用这项技术的设计能力。另外,事实证明,理解 EJB 相关的设计范式就是它的丧钟。

3.吸取的教训

面向对象的程序设计和EJB都曾经被视为有前途的技术,但事实证明,它们并没有达到最初所炒作的那样。问题不在于面向对象编程语言或实现EJB规范的平台,而在于我们怎样使用这些技术设计应用。最大的挑战是关于设计范式的。

这些教训为 OSGi 可能会面临的艰巨挑战提供了前车之鉴,具体来讲就是 Java平台的模块化。如果开发人员不能很好地理解设计范式以及指导使用这项技术的原则和模式,那么运行时模型的优势将不能实现。要设计在架构方面灵活和适应性强的软件系统,模块化是一个关键因素。在 Java 平台中,确实存在模块化的需求,尤其是在开发大型企业级软件系统时。但是,如果现在不立即着手理解怎样设计更加模块化的应用,那么当支持模块化的平台出现时,我们将会面临重大的挑战。

2.3 模块化现状

乍看起来,运行时模型与开发模型有着密不可分的关系。运行时模块系统会使得设计模块化的软件更加简单,但对于设计模块化软件,它并不是必需的。实际上,本书第一部分的其余章节会讨论模块化所带来的明显益处,在了解完这些之后,开发团队最好可以立即模块化他们的应用,即便它们目前可能不会部署在支持运行时模块系统的平台上。如果你正在使用支持模块化的平台,那这对你会很有好处。但如果不是这样,那么你可能会想知道如何设计模块化的软件。

关于 OSGi 运行时模型,我们了解了很多,Jigsaw 的细节也逐渐为人所知。我们正在使用的很多平台在内部使用了 OSGi 运行时模型,即便它们现在并没有向我们暴露这一点。我们已经知道模块化单元是 JAR 文件,通过强调将 JAR 文件作为模块化单元并使用本书中的模式,现在就可以模块化我们的应用了。在第7章中,我们会直接看到在没有运行时模块系统的情况下,如何将一个系统进行模块化,这是通过将 JAR 文件作为模块化单元实现的。本书中很多模式的样例有意避免使用 OSGi ,不过我们会讨论当使用OSGi时这些模式该如何实现,OSGi 会帮助我们进一步解释它们。在很多场景下,我们会包含使用 OSGi 的示例来阐述它所带来的优势。

当在应用中使用 OSGi 时,还需要记住的一点就是,对应用进行模块化的目的不仅是开发利用 OSGi 的应用,真正的价值在于它所带来的模块化架构。

注意

有很重要的一点需要注意,在没有运行时模块支持的情况下,对软件系统进行模块化会很有挑战性。在没有运行时系统时,你所使用的工具就很重要了。可视化工具可以帮助你理解结构,构建工具可以强制你保证应用中的结构,而依赖管理工具可以帮助你管理依赖,这些工具是很必要的。在本书中的很多样例中,将使用 JarAnalyzer 管理模块间的依赖(关于模块依赖,参见 4.3 节)。

另外,本书中的模式会帮助你对系统进行模块化。例如,等级化构建模式(Levelize Build pattern,参见 12.2 节)会帮助你在编译时实现模块间的关联关系。借助良好的工具和严格的纪律,模块化架构就会实现。另外,模块化架构实现之后一旦有运行时支持,就可以马上使用它了。但是,如果没有运行时支持,有一些事情是无法完成的。运行时模块化系统通常会支持以下的功能。

  • 封装(encapsulation) :在标准的 Java 中,包中任何公开的类只要在类路径下就能被类路径下的其他类访问。换句话说,没有办法隐藏实现细节。所有的事情都是全局性的,这阻碍了模块化设计。运行时模块化系统提供了隐藏实现细节的能力。例如,OSGi 可以使用µService 为底层的实现细节提供接口。

  • 动态部署(dynamic deployment):在标准的 Java 中,更新软件通常需要重新启动 JVM。运行时模块系统支持热部署。

  • 版本管理(versioning):在标准 Java 中,不能为一个类部署多个版本。模块化的系统允许部署多个版本。

  • 依赖管理(dependency management):在标准的 Java 中,没有办法实现模块的依赖结构。像 Maven 这样的构建工具试图通过描述依赖关系的 JAR 文件仓库解决这个问题。运行时模块系统可以实现运行时的依赖管理。

2.4 结论

模块化有两个方面:运行时模型和开发模型。开发模型包括编程模型和设计范式。所有的方面都是很重要的,但是如果不能理解如何设计模块化的软件将会降低使用模块化运行时或框架所带来的益处。本书主要讨论的是如何解决设计范式的问题。


wmz 2015-08-03 08:37

推荐一个比较优秀的模块化、插件化开源开发平台 JXADF:http://osgi.jxtech.net

顶(25) 踩(0) 回复

泪雨迷情 2013-11-20 13:13

这个不错!!

顶(0) 踩(0) 回复

兴华 2013-11-05 11:37

本章看完,还是很有帮助的,以前理解的不系统,继续看下一章.

顶(0) 踩(0) 回复

张卫滨 2013-10-10 16:24

回复北海飞舞: 是的 不过作者在书中有不少的章节展开讲这些内容,希望你能中奖,呵呵

顶(0) 踩(0) 回复

北海飞舞 2013-10-10 15:23

读完文章,感觉设计范式与编程模型挺重要的,但是只是概念太抽象了,不太好理解

顶(0) 踩(0) 回复
查看评论