程序原本(七十九):应用开发基础——应用程序设计语言的复杂性(模块化思维在产品交付组织形式上的延伸:插件机制)

阅读数:71 2019 年 10 月 3 日 14:36

程序原本(七十九):应用开发基础——应用程序设计语言的复杂性(模块化思维在产品交付组织形式上的延伸:插件机制)

对于一个应用程序来说,“插件”往往意味着它是一个动态链接库、静态链接库,或一个独立发布的脚本包。如前所述,库或包,只是一堆代码在组织上的措辞而已。因此插件的本质在于用组织形式来解决空间因素所致的复杂性7

7 这仍然是“第 12 章 应用开发技术”所述观念的具体实践。

“插件”这一方案意味着插件的宿主(HOST)与插件本身(Plugins、Addons 或 Extensions 等)是分别发布的。这事实上与应用容器有一定的相似性,只是两者应付的问题集存有稍许差异而已:

  • 应用容器主要提供可配置的、可重新实现的环境,并通过标准化来提供跨业务领域的应用支持。尽管应用容器也有应用范围的限制,但通常该范围的边界更大。
  • 插件的宿主通常在产品、用户和功能这些方面基本确定,在技术方案上也通常是预先可确定的,因此“宿主 + 插件”更适宜作为一个具体应用产品的实现手段。

与应用容器类似,宿主也有跨平台的问题,例如 Firefox 浏览器。这个问题有两个解决方案:其一,如果产品的用户有能力在不同平台上编译并重新发布宿主8,或者宿主是针对特定平台开发而无需重新发布的,那么插件通常实现为与环境相关的二进制模块;其二,如果宿主有明确的跨平台需求,而其编译过程复杂或不宜公开编译方案与代码,或者用户没有能力或没有必要去区别、编译不同平台中的插件版本,那么插件通常实现为文本形式的脚本或预先编译为跨平台的中间代码 / 代码包(前者例如 Firefox 浏览器中扩展名为.xpi 的插件,后者例如 Java 的.class 文件)。

8 例如 Apache 采用“WWW 服务器 + 扩展库(动态链接库形式)”的形式来交付,一方面是考虑到二进制模块的执行效率,另一方面也因为编译发布是部署人员应有的技能。通常来说,Linux 用户会更多地面临这一类情况。

插件技术是基于应用开发技术的“模块 / 单元”理念而来的。不同的是,宿主通常可以脱离部分或全部插件独立运行,而应用程序通常不能独立于模块 / 单元来完成编译。能否从“模块 / 单元”中去除强约束的依赖性,是识别能否代之以插件技术的可行方法。

如前所述,在面向对象的设计观念中,“类”替代了“function/unit/module…”等组织方式。因此在面向对象的设计中,“HOST 类 + 插件基类”是这一解决方案的基本设计。更进一步地,由于名字空间用于应付更大规模的组织,并且通常也具有“作用域”限制的能力,所以使用“HOST 框架 + (插件的)名字空间”也是一种可行的实现。这在本质上与前一种设计没有不同,只是应用的规模更大,而且作用域范围控制更为灵活,类似安全性、账户等级限制等特性也往往更加容易实现。

但是基于面向对象的插件技术的问题在于:通常宿主与插件都基于相同的“面向对象语言”,因此具体的对象实现技术、二进制编译技术、对象封装技术等限制了插件的通用性。例如很难用 Delphi 开发一个“类”,并将它作为一个插件用在 Visual C++ 开发的宿主上。

这个问题的核心在于如何实现“组件复用”。这里的“组件”(Component)与此前的“模块”的区别在于:组件通常用于表达面向对象中的可复用对象 / 类,而模块通常用于表达结构化编程中的一个可复用件9。这两者所面临的问题是完全一致的,其解决方案也有着延伸关系,例如 Windows 中的 COM 复用与 DLL 复用其实是一脉相承的,而.NET 中的 Assembely 复用与 COM 复用也是类似的关系。

9 并不一定是源代码中的一个单元(unit),而是指在指定的应用开发环境中,由语言或平台决定的“可复用单位”。

这通常涉及三种不同类型的复用(事实上划分它们的依据也并非唯一):

  • 二进制复用,例如 COM;
  • 公共指令集复用,例如 JAVA 类;
  • 文本复用,例如脚本。

无论技术方案的提供商如何渲染这些技术,其最终的结果是一样的:开发人员终于可以用 Delphi 写一个 COM 组件,并让它运行在一个 Visual C++ 写的类工厂中;或者用任意的 Java 编译器编译一个.class,并分发给其他编译、运行环境下的宿主。当这一切成为现实10之后,组件技术的核心便集中在了宿主框架的设计、插件的加载机制,以及隔离宿主与插件之间或多个插件之间的安全性这几个方面11

10 我的意思是组件复用成为应用开发环境的一个内置技术,例如 Java beans。

11 尽管并不明显,但事实上这种隔离通常与消息机制有关。无论是从“消息”这一概念的历史还是从它在应用系统中的使用来说,它都是“体系性”相当明显的一种技术。因此本书将在“编六:系统的基本组织方法与原理”中去讨论它。

插件提供了通过剪裁功能来重新定义产品的不同版本(例如标准版、专业版)的能力。与此同时,正因为它在概念上继承自“模块 / 单元”,所以也是一个“化整为零”的典型方案,这使得在大型项目中实施持续集成具有了可能性。这其中的一个有趣的事实是:如果集成需要所有“模块 / 单元”的参与,那么集成的失败率就会在系统规模达到一定程度后出现级数性的增长;只有被集成的产品可以通过类似插件、可复用件的机制,将单一部件的规模控制在一定范围内,持续集成——而不致阻碍整个 Team 的推进——才成为可能。

评论

发布