程序原本(二十七):语言及其面临的系统——从功能到系统(方向 2:程序组织的结构化(从模块化到产品化))

阅读数:30 2019 年 9 月 28 日 18:10

程序原本(二十七):语言及其面临的系统——从功能到系统(方向2:程序组织的结构化(从模块化到产品化))

问题 3:语法与语义上的特性,是否决定了该语言可能适宜的开发规模?

在第三个问题上的尝试,推动了结构化思想在另一方向上的发展9,即通过在程序组织上的结构化来解决规模问题。这一方向上的主要动力,是程序所应对的需求渐渐扩展到非专业领域,用户的入口变得多样,甚至同一功能涉及到多种用户 / 角色的、不同时间与时序上的参与。这些也是软件开发工作的成果从专属程序进化到软件产品的主要标志。

9 我并不确定 Dijkstra 所述的“组织和编排程序”究竟在何种程度上影响了后两种等级的语言出现,但我确信这种思想是导致它们出现的原因之一。

我所观察到的第一个与此相关的语法元素是单元(unit)10。因为一段代码可以写得任意长,也可以拆成几个单元,所以这些单元存在与否,就与这段代码要表达的计算功能是完全无关的了——既不是对运算的增强,也不是对数据的增强。接下来,我们发现一些单元可以对许多不同的 function/program 的开发提供支持,由此库(library)的概念就形成了。“库”用于集中管理一些具有相同应用范围、处理相关数据、解决相似问题的单元以及单元对外部声明的界面(interface)。如图 4 所示,这些语法元素之间的关系非常简单。

10 这里指的是 Pascal 语言中的单元 (unit) 概念。具体到其它语言的实现上,它也可能被称为模块 (module) 等等,例如 Erlang。

图 4 单元、库与界面间的关系

程序原本(二十七):语言及其面临的系统——从功能到系统(方向2:程序组织的结构化(从模块化到产品化))

大多数情况下,库与库之间的区别仅在于功能集不同、应用范围不同、接口方式不同,以及我们经常关注的效率等的不同。(不考虑这些外在的表现)这些库内在的抽象思想与应用价值是完全相当的。

库的出现带来了一些组织程序的策略,例如静态链接库(Lib,Static Link Library)、动态链接库(DLL,Dynamic Link Library)、类库(Class Library);又例如跨语言的组件库(COM 库、Component 或 Component Library)、公共程序集(Common Assembely);再例如,远程 / 跨平台对象(Remoting Object,或 XPCOM)等。这些策略被种种语言实现为不同的、语言内置的关键字,并由此决定了各自不同的一套支持语法。

所有这一切的目的,仅仅是因为那个最原始的发现:总有一些代码与当前处理的业务没有实际关系,可以被隔离到其他代码块(例如 Unit)中去。既然从组织策略来看这种隔离必然会发生,那么我们关注的问题就仅仅是上图的 Library 对外表达的形式(亦即是接口 /Interface),而非 Unit 或类似语法元素的实际实现11

11 这一观点的得来决非易事,它是 20 世纪 70 年代多个程序设计流派之间的主要争端。David Parnas 的“信息隐藏”的观点取得了最终的胜利,即模块内部的数据与过程,应该对不需要了解的信息予以隐藏。Brooks 最初持不同的观点,但在 1995 年的《人月神话》二十周年版中,他坦承“关于信息隐藏的观点,David Parnas 是对的,我是错的”。

这些变化既有语言进化的内在动力,也有软件规模变化这一外因的推动。当计算机不再仅是用于计算的工具,而变成软件产品的运行平台时,整个软件——传统意义上的程序(program)——的构成已经发生了明显的变化。现在,它必须面临的问题包括三个方面,如图 5 所示。

图 5 面临的问题与背景:Application 在“结构化”上选择不同方向的原因

程序原本(二十七):语言及其面临的系统——从功能到系统(方向2:程序组织的结构化(从模块化到产品化))

也就是说,即使程序——我们仍然可以用第二个等级中的“程序”(program)来指代上图中的一个方面——在功能上的规模没有任何变化,那么也将面临如下两类需求:

  • 使用人群由专业人员变成普通用户,将导致提出大量的非功能需求,即它作为“软件”的使用问题;
  • 由于使用人群的变化,维护和发布的工作对程序员变得不可控,因此将导致提出大量的非当前需求,即它作为“产品”的生命周期问题。

这二者,也即是“软件产品”之于“程序、功能”的区别。举例来说,在最初的操作系统中,我们开出一个内存块来使用。

  • 首先,我们仅仅是需要在别的计算过程中使用到它,因此它被实现为一个功能(function)12

12 这个功能基本上可以概括为:基于存储地址、用于限制访问边界、纯粹的线性计算(分配)的过程。

  • 接下来,操作计算机的专业人士认为,其实可以从内存中拿出一块来并将它 mount 成一个虚拟的磁盘,于是将它写成了一个“简单的”程序(program)13

13 这可能是一个有 150 多个可选配置项的命令行程序,除了--help参数就再也没有其他任何称得上文档的东西。

  • 之后,某个并不太懂计算机的桌面用户也需要上述功能,但受限于他的计算机操作水平、实际的应用环境14,他必然会在上述程序、功能之外,提出前述两类需求。

14 作为产品的用户,他还期望在个人需要与应用环境都发生变化时,仅支付少量的代价即可应对,而非重新购置。

对于这类(软件产品的)用户,我们称支持非功能需求与非当前需求的程序为应用(application),而将这一等级上的语言统称为“应用程序设计语言”(application programming language)。这些需求促使一个应用中包括了多个领域的产品功能,一些显而易见的内容包括:

  • 与产品使用相关的界面,例如 3D 操作、游戏界面、数字监控仪表盘等;
  • 与应用环境相关的业务逻辑规则,例如企业组织架构、工作流、金融、期货等;
  • 与操作平台相关的开发接口,例如 ATL、Android SDK、GTK+ 等;
  • 与发布、维护相关的支持,例如帮助文档、安装包、工程文件管理、代码文档化等;
  • ……

这些内容的领域性特点使得任何一个或一类程序员都无法完全胜任它们的开发工作。单元等类似手段以及模块划分时需要隐藏内部信息的思想,都是在这样的背景下产生的。其产生的必然性,是泛计算领域15本身的纵向隔离的特性所决定的16。也就是说,既然这是软件需求从专业计算领域走向泛计算领域带来的规模问题,那么也要求程序在组织上的抽象必须能表达和匹配后者在领域问题上的纵向切分。

15 但凡一切可以抽象为可计算对象,并通过计算系统来解决问题的领域。

16 最简单而又直白的说法就是:隔行如隔山。

评论

发布