在组装应用程序的时候,需要将各个独立的模块整合在一起。这些模块之间需要相互通信,通常来说,是通过定义好的API来完成的。每个组件的API都能增加你迈向无绪的步伐。不必了解组件内部机制,只阅读该组件的文档,调用其API就能够使用该组件的功能。利用API可以避免去了解第三方组件的细节。
抽象也会泄漏天机
API可以被看作是对功能和组件内部实现的抽象。通常来说,外部人员只要了解抽象即可,但在某些情况下,一个API也可能会“泄漏”内部组件实现中的一些信息。来看一个文件系统抽象的例子。通用的文件系统将文件和目录以树状的层次结构组织在一起。在获得一个资源以后,可以访问其内容②,或该资源的子资源。资源的内容通常是使用流方式③来提供的。现代操作系统几乎都使用了这种方式来支持对文件内容的访问。这样在开发时,程序员就不需要去关注实际的存储和文件系统的类型。在读取文件时,不需要知道该文件是放置在硬盘驱动器、光盘、闪存盘中,还是网络上。不同的存储介质以这种通用的抽象提供了一致的处理方式。
然而有时候,API却会把这些具体实现的相关信息泄露出来。比如说,在读取闪存盘上的内容时,可能会因为用户拔下了闪存盘而使得正在访问的资源突然消失了。或者在使用网络存储的时候,可能会因为网速很慢或者网络性能差,使得文件资源的访问有很长时间的延迟。在这种情况下,开发人员不仅要了解抽象的信息,可能还要了解具体实现的部分内容。
但从“针对性无绪”的角度来说,这样做也是可以接受的。这种通用的API只是一个基本的抽象,它提供服务,只要用户不在意网络延迟,或者移去闪存盘时引起的崩溃。如果你在意,还有一些高级的方式来检测处理这些情况。因此文件系统API可以让程序员做到无绪,而且也不会影响我们在必要情况下深入了解系统信息。任何一个对外提供功能的API都应该做到这一点。
一个典型的应用程序不仅仅是由一个或者几个类库组成的。当今开发的应用程序,会用到全世界开发人员提供的很多开源功能库,这些开源贡献始于Unix内核、C语言基础功能库和命令行工具。发展至Web服务器和Web浏览器,以及Java实用程序,如Ant、Tomcat、JUnit和JavaCC等。事实上,每个类库都有自己的API ,因此每个写这种软件的人都在从事API的设计,不论他自己意识到没有。
对于各种Linux发布版来说,将模块集合在一起打包,然后发布出去,这是常见的一种组装方式。软件是由不同的人编写的,然后再通过编译、打包,最终整合在一起。通常,不同Linux版本的发行商只需要写一个中心管理器的模块,用来提供质量保证,让所有选中的软件都可以正常运行。对于大部分供应商和用户来说,这种方案行之有效,能够降低创建Linux分发版本的成本。这种模式还有一个成功的案例,就是Mac OS X系统,它是在一般FreeBSD Unix版本上添加了一些由Apple公司开发的功能模块。
分布式开发自有其特殊之处。最大的不同就是,整个程序的源代码已经不再完全处于开发人员的控制之下,而是散布于世界各地。与完全基于内部代码仓库④中的源代码来构建一个程序相比,这种构建软件的方式是明显不同的。
我们需要认识到,在这种开发模式下,产品的开发进度是无法全面掌控的。软件的源代码和开发人员都散布在全世界,而且开发人员各有自己的进度安排,所以项目领导者对此无法一一掌控。猛一听,感觉项目完全不可控,但真实情况并不像乍听起来那样危险。任何一位项目领导者,只要其带领的团队超过50人,那么他一定明白,所谓的全面掌控往往只是一种理想状态。在开发过程中,考虑到时间、资源等因素,有时不得不砍掉某个功能或者发布一个老一点的版本⑤。对于分布式开发模式来说,每个人在使用功能库的时候都可以自由选择使用老版本还是新版本。
事实上,要设计良好的API,开源并不是唯一驱动力。商业组织也提供了大量共享的功能库和框架。还有不少公司提供了一些现有标准的具体实现产品,如各大数据库厂商的产品都支持SQL标准,还有一些厂商提供了自己的API⑥。带有自由许可证方案(liberal licensing schemes)的开源运动已经成为组件的首要来源,这些组件可以作为可复用构件来使用。最终用户知道很多开源软件方案,不需要支付任何费用即可使用。而且开源软件没有许可权的限制⑦,这对于开发人员来说也是很重要的。很容易就可以根据自己的需求来找到一个组件,然后把这个组件用作自己应用程序的一部分。迟早会有人这么去做。这也说明每一个开放源代码的组件迟早都应该有一个API。这些通用的组件是由各类程序人员开发的,可能是刚开始做项目的大学生,也可能是能够投入大量时间在某个项目的开发人员,还有可能是某些公司专门雇用的开发人员,他们看到了开源软件的商业机会。这些开发者有着不同的开发技巧,不同的开发方式。无论如何,设计优秀的API是一件非常重要的事,因为API是无绪使用库的开始。类库和框架这类东西自然是越多越好。但要想让无绪重用能够成功,就必须让API体现库的内在功能。这就是为什么API的设计如此重要,成为本书写作的主要动机。
① 即Rich Client Platform,缩写为RCP。——译者注
② 如果是文件,就访问文件内容,如果是目录,就访问其子资源。——译者注
③ 在Java中,即常用的InputStream和OutputStream。——译者注
④ 这里的代码库其实就是暗指常用的版本管理系统。——译者注
⑤ 指到了指定版本发布时间,可能最新的版本尚不能稳定或者目标没有达到,所以不能正式发布,只能将前一个内部的稳定版本发布出来。——译者注
⑥ 如JDBC和ODBC都需要对外API。——译者注
⑦ 原书是这样说的,但事实上,开放源码并不是意味着就可以免费使用,很多开源软件都是双协议,对开源软件或者非商业软件都是免费的,但商业用途并不免费,如著名的Ajax框架Ext,所以原书中说的并不是很准确。
——译者注