逐步改善,设计优秀的API

文/
判断一个API是否优秀 , 并不是简单地根据第一个版本给出判断的 , 而是要看多年后 , 该API是否还能存在 , 是否仍旧保持得不错 。
第一个版本远非完美
第一个版本总是来得特别容易 , 不仅容易开发 , 而且容易发布 。API的需求会随着时间而变 , 那些过去有效的API可能现在已经不再适用了 。而且每个程序中都会存在Bug , 需要不断地来修复 , 这样做带来的副作用人所共知:修复一个Bug的同时会引入两个新Bug 。这些观点普遍适用于所有软件系统 , API也不例外 。
但我们没必要为这个结论而感到悲观 。API因为需要不断改进的事实算不上什么坏事 , 只是对现实的一种坦诚 。每一个API的作者都应该为未来的改进做出计划 。这种计划是一种比较高层次的 , 要考虑未来版本会对API中哪些内容加以改进 。这种计划可能会用到两种方式 。一种极端的方式是放弃老的版本 , 重新开始做一套新系统 。还有一种方式则是修正用户提出的问题 , 并强化现有的API , 保证兼容性 , 从而使得现有客户端的功能不会有所改变 。
放弃现有的API , 并从头开始编写一个新的API来完成同样的任务 , 可以避免不兼容问题 。这样做唯一的问题就在于:那些使用旧API的客户端只能继续沿用老的API , 除非重新编写他们的代码 , 以升级到API的新版本上 。所以这样做的缺点也是不容忽视的 。
完全重新编写API的优点在于避免了细微的不兼容问题 , 但让客户端被锁定在一个特定的版本中 , 即使新的版本提供了大量的改进 , 这些客户端也无法从新版本中获益 。虽然对API进行改进固然是一件重要的事情 , 但相比之下 , 兼容性更为重要 。只有在这两者之间巧妙地取得平衡才能让一个API成为可用的API 。
向后兼容
对于每一个API的设计者来说 , 都渴望做到“向后兼容” , 因为不管是现在的API用户 , 还是潜在的API用户 , 都只信任那些可兼容的API 。但向后兼容有多个层次上的意义 , 而且不同层次的向后兼容 , 也意味着不同的重要性和复杂度 。
源代码兼容
说到兼容性 , 最先要面对的问题 , 就是保证源代码编译时的兼容 。如果基于Java 1.3版本开发程序 , 那么可以用Java 1.4版本来编译这些程序的源代码吗?如果能做到这一点 , 那么可以说Java 1.3和Java 1.4这两个版本是源代码兼容的 。但源代码兼容是非常难以达到的 。之所以出现这种问题 , 主要是因为每个新版本的Java语言都会添加一些语法上面的新功能 , 这种改变往往都会体现在执行文件的格式上 , 也就是Class文件的格式会有所调整 。
二进制兼容
如果一个基于老版本类库开发的程序 , 在不需要重新编译的前提下 , 可以和新版本类库进行正常连接并执行 , 那么这种情况可以称作二进制兼容 。因为有两种场景需要这种兼容性方面的支持 , 所以要做到这一点也是非常重要的 。首先 , 用户基于某个版本的类库编写了一个程序以后 , 原先开发的程序应该都可以一直正常运行 , 不管用户手中的类库是哪个版本 , 是否升级到了最新版本 , 程序的运行都应该是正常的 。这样做可以极大地简化程序的维护、打包和发布工作 。其次 , 如果用户只有一个老版本的二进制类库 , 也同样可以开发程序 , 随后再移植到新版本上 , 这样就无须用户来重新编译程序 。这两种场景都有各自的用途 , 它们提升了配置方面的灵活性 , 并赋予模块开发人员和用户更多的自由 。为了达到这种相互调用的灵活性 , 开发人员至少需要了解一些源代码编译后生成的二进制格式 。对于Java语言来说 , 就表示开发人员需要去了解Class文件的格式 , 以及Java虚拟机如何加载Class文件 。