程序原本(七十八):应用开发基础——应用程序设计语言的复杂性(对应用与应用容器进行标准化,是类似集装箱的一体化解决方案)

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

程序原本(七十八):应用开发基础——应用程序设计语言的复杂性(对应用与应用容器进行标准化,是类似集装箱的一体化解决方案)

应用程序并不是直接运行在机器环境中的,它通常依赖操作系统,甚至是依赖操作系统的具体版本。严格来说,所谓“操作系统或其具体版本”所提供的,就是一个运行环境。基本的操作系统主要封装了对硬件系统的访问,这包括:

  • 存储(内存、硬盘以及移动存储管理);
  • 标准 IO 设备(显示器、键盘、鼠标、打印机等);
  • CPU 及其分配(进程与线程管理等);
  • 网络、媒体、通信等。

除此之外,操作系统提供文件以及其他操作系统资源的访问。2总的来说,操作系统为上述内容提供的 API(应用程序接口,Application Programming Interface)可以理解为运行环境的界面。但多数应用程序不单单依赖这个环境,例如使用 Visual C++ 编译的应用程序还依赖 Microsoft 发布的 VC++ 运行库3

2 并不是所有的操作系统都提供文件访问支持,这取决于该系统对于“操作系统 (的) 资源”的定义。例如 Windows 系统中可以将注册表理解为资源,而 Linux 系统中就没有注册表。

3 对于 Microsoft 产品来说,操作系统中通常会带有该运行库的一个或多个版本,但某些情况下,仍然需要应用程序自己来管理这个库的安装与维护。

应用程序语言中的许多编译指令、伪指令、条件编译指令,甚至配置信息文件等,都可以用来做环境设定以及依赖分析,简单的如汇编语言中著名的伪指令:

复制代码
.386

而复杂的则类似于 Linux 系统中的 Make 文件。

Make 文件也有很多种类,而且并不仅仅出现在 Linux 系统或某些面向 Linux 系统的语言中,事实上几乎所有稍具规模的应用程序语言与开发环境都有自己的 Make 程序和 Make 文本。为了提供更为复杂的 Make 功能,许多 Make 文本被设计为“另一种语言”,也带有逻辑、条件、变量等语言特性。但这些特性并不是相应“应用程序开发语言”的组成部分,而仅仅是用来对“这个程序的产生、分发等过程中的一些需求”加以规则化、程序化,因此我们通常也称之为 Make 脚本。

通过对规则的抽取、细化以及提供脚本化的支持,“越来越复杂地 Make”成了一个发展方向,几乎所有在“运行之前”能预料到的需求都可以通过下列技术来解决:

  • 再编译一次;
  • 分发不同的编译版本;
  • 提供独特的安装过程;
  • 特定的运行期环境依赖声明;
  • 调整程序的启动参数。

然而这种方案有三个致命的弱点4:其一,我们不可能预料到问题的全集;其二,我们为可能出现的问题所付出的代价,甚至要多于编写应用程序本身;其三,由于这一方案(从逻辑上来讲)容许无度的需求,因而可能因次要特性过于丰富而导致最终产品失控。与这样的运行环境相对照的是,应用容器通过“将应用限定在一个明确设定的环境中”有效地规避了上述问题5——正因为环境特性由容器来决定,所以应用程序的开发与使用都不会超出容器提供的特性集的范围。

4 这些弱点的表现之一是:Linux 下繁杂的产品生态(例如不同的编译、发布与维护版本),以及非功能性需求的实现过于复杂(例如安装)。

5 运行环境与应用容器两种方案,在解决问题时所处的层面是不同的。前者是从系统的视角出发来限制性地提供应用的背景,后者是从领域的视角出发来提供对应用范围的假设。

应用容器通常明确地设定了应用的类型,例如 Web 应用容器或企业应用容器等。容器会根据应用的需求来搭载相应的可选件,并通过统一、一致的接口提供给应用程序使用。这些选件与接口被标准化,变成应用容器整体方案的一部分。更进一步地,在这些范围确定、接口标准的方案周边,部署方案、优化方案、硬件配置方案、商业应用方案等渐渐地得以形成。

对应用与应用容器进行标准化,勾画了一个类似于“集装箱”6的王国,EJB(Enterprise JavaBeans)是这个方案的成功范例之一。EJB 的特性以及通用应用容器的规范,其本质上仍然是在“规则化”这一方向上,它们通过:

6 参见《集装箱改变世界》,Marc Levinson 著。

  • 编译或运行指示,例如注解(Annotation)、验证器(Validator)、配置文件;
  • 脚本化,例如事务脚本(Transaction Scripts)

等技术来实现“将非功能性需求限制在特定范围”。

评论

发布