保险杠贴纸上的 API 设计指南
本文转自 Joshua Bloch 在 InfoQ 上发表的文章,翻译为本人提供。
所有的程序员都要设计API.
好的程序是模块化的,模块之间的边界就是 API. 好的模块会更多地被重用。
API 可以是你最大的资产,也可能是你最重的负担。
好的 API 引来长久的客户;糟糕的 API 带来长期的支持梦魇。
公开的 API 就像钻石,是永恒的。
你只有一次机会把它做好,所以要竭尽所能。
API 应该容易使用,而难以误用。
应该能很容易地做小事;具备能力做大事;不能,或者至少很难做错事。
API 应该自带文档属性。
好的 API 产生的代码,应该不需要多少文档就能阅读。实际上,写这样的代码也不该需要什么文档。
设计 API 时,应该先收集需求,但要保持一定的怀疑心。
人们常常会向你提供解决问题的方案;你必须透过他们的方案看到问题的本质,并给出最好的解答。
将需求整理成用例。
这些用例会是衡量你 API 设计的标杆。
API 设计的初稿应该精炼短小。
算上类名,方法签名,以及一句话的描述,应该在一张纸内。如果你第一次没搞对,这会使得重组更加简单。
在实现 API 之前,就先用你的用例测试。
甚至在具体深化定义之前就测试。如果设计本身就行不通,这会省下你大量的实现与定义时间。
把测试用例的代码一直留着。
这可以避免后面维护中突然出现问题,而且这些代码会成为 API 的示例代码,教程的材料,以及测试案例。
示例代码应该是大家的榜样。
如果 API 被大量使用的话,其示例代码会成为千百个程序员的范本。任何错误都会对你带来千百倍的伤害。
你没法让所有人都满意,所以目标应该是让每个人都同等地不满意。
多数 API 都相当收敛。
设计的错误总是会有的,因为我们的想象并非万能。
我们不可能想到每个人拿我们的 API 做的每件事,也不可能想到它与系统的每一个组件的交互方式。
API 设计不是一个人的战斗。
把你的设计给别人看,越多越好,认真倾听他们的反馈。你没想到的情况,可能别人看得一清二楚。
不要设置固定的输入规模限制。
这会影响它们的可用性,并加速它们的淘汰。
名字很重要。
追求可读性、一致性、对称性。每个 API 都是一个微小的语言,人们要学习去读与写它。如果设计做得好,代码读起来会像谈话一样顺利。
如果找不到合适的名字,就要重新来过。
不要害怕拆分或合并 API, 或者将其放到更广泛的语境中。如果不假思索便能想到名字,那么说明你在正道上了。
如果不确定,就不要上。
如果 API 设计有一条根本大法的话,这就是了。这对功能、类型、方法、参数都同样适用。API 设计的每个方面都应该在可能的范围内尽可能小。你以后总可以添加新的内容,但是你没有办法把既有的东西去掉。把概念缩小,比类与方法的数量更重要。
不要在 API 里面牵涉实现细节。
这会迷惑用户,并且限制其在未来发展的灵活性。具体什么是实现细节,并不是很清晰。但要对过度定义持怀疑态度。
尽量减少可变性。
不可变的对象更简单,线程安全,而且可以随意共享。
文档很重要。
不论 API 设计多么优秀,没有好的文档,它是不会有人用的。为每个公开的 API 元素都编制文档:每一个类、每一个方法、每一个字段、每一个参数。
要考虑 API 设计决策对性能的影响,但是不要为了性能提升而扭曲一个 API.
幸运的是,好的 API 一般都不妨碍高性能的实现。
入乡随俗。
API 应该与其平台和谐共处,所以要遵守平台的规范。把一个平台上的 API 直接翻译到另一个平台的做法几乎总是错误的。
把访问级别缩到最小。
如果不确定,就设为私有。这会简化 API 并降低耦合度。
如果没法理直气壮地说子类的每个实例都是基类的实例,那么就不要继承。
公开的类永远不能只为了重用实现就继承另一个类。
面向继承设计与编制文档,否则就不要继承。
这样的文档应该遵循自用模式:一个类中的方法如何使用其它。如果不这样,安全的继承就无法实现。
你的库能做的事,不要交给用户来做。
不然,用户就需要写大量的重复代码,既烦人又容易出错。
遵循最小惊讶原则。
一个方法应该做其名字所指示的事,不要让用户惊讶。如果方法做的事与用户想的不一样,就会产生错误。
遇到问题赶快报错。
报错的时间越早,造成的损害就越小。最好是在编译时就报错。如果必须在运行时报错,那么就越早越好。
对于所有可以通过字符串获取的数据,都提供程控访问的方式。
否则,程序员就必须解析字符串,这会很痛苦。更糟糕的是,这些字符串的形式实际上也会成为 API.
重载要谨慎。
如果两个方法的行为不一致,最好是指定不同的名字。
用合适的数据类型。
例如,如果有更合适的类型,就不要使用字符串。
各方法的参数顺序要统一。
不然程序员会搞反。
避免过长的参数列表。
尤其是那种连续几个参数类型都相同的。
避免返回需要特别处理的值。
用户一定会忘掉这些特别情况的代码,导致程序出问题。例如,不要返回空引用,而要返回长度为零的数组或集合。
只在非常情况下才抛出异常。
不然的话,用户就必须在正常的控制流中使用异常,这样的程序难以阅读,容易出错,性能也不会好。
只有用户真正能够从异常中恢复时,才抛出检查异常。
API 设计是艺术,不是学术。
追求美感,相信自己的内心。不要死抠上面的规律,在有充分理由的时候,可以偶尔违背它们。
2019 年 11 月 22 日更正:
原文
不可变的对象更简单,
类型安全,而且可以随意共享。
应为
不可变的对象更简单,线程安全,而且可以随意共享。