API 设计二三事
所有程序员都是 API 设计者。
很认可 Dan Webb 的这句话。近期刚好也有一些思考,总结分享下。
可预测性
jQuery 很优秀,原由之一就是其 API 具有很强的可预测性。比如我们看到这样一段代码:
var container = $('#container'); container.append('<p>paragraph</p>'); container.append($('#some')); // snip...
会想是否可以写成:
var container = $('#container'); container.append('<p>paragraph</p>', $('#some')); // snip...
一试,果然可以,很让人欣喜。这样,不用详细阅读文档,根据直觉,就可以推测出 API 的一些高级用法,并且记忆深刻(因为是自己探索发现出来的)。
我们可能会好奇,是什么让我们拥有这个“直觉”?这和个人的编程经验甚至生活经验有关。比如对我来说,是因为用过 Array.push 的“高级用法”:
var items = []; items.push(1, 2, 3);
人类大脑的联想功能非常强大,看似不相关的一些体验,在暗藏的潜意识里会汇聚交融。这简直让我想转行去研究大脑的运行机理,太迷人了。
联想与个体的经验相关,这意味着有些代码,在不同个体里触发的联想不一样:
var words = ['first', 'second', 'third']; var result = []; for (var i = 0; i < words.length; i++) { result.push(words[i].toUpperCase()); }
上面这段代码,会让你产生怎样的重构思路?可能会想起 forEach, 我第一直觉里联想到的是 map:
var words = ['first', 'second', 'third']; var result = words.map(function(word) { return word.toUpperCase(); });
注意 map 在 IE 低版本里不支持,我们可以用 underscore 来封装。
上面的例子很简单,但足以说明问题。更复杂点的,比如 reduce, yield, let 等等,一旦我们接触过,感受过其强大,就会不知不觉想随时随地享用之,并且觉得很自然,觉得本就应该如此。
这就是语言学习上的技术视野。在我幼年时,肉是很奢侈的。家乡有句俗话:“没吃过猪肉,总见过猪跑”。我小时候见过各种猪跑,因此对猪的视野很广。现在专职搞 Web 开发,也得多看看猪跑,不必太深入,比如 yield 的深层机制,除非你真的感兴趣。
扯远了,回到本文主题。
只戴一顶帽子
很喜欢霍华德•毕哈在《将心注入》一书中的这个比喻。在我们的职业发展道路上,我们要弄清楚自己的定位,要保持专注。“只戴一顶帽子”,能让我们在专业道路上走得更远更深。在 API 设计里也是如此,比如听得耳朵起茧的职责单一原则,强调的也是别给一个类或一个方法戴上多顶帽子。
jQuery 1.6 有个很重要的非兼容性变化:
>input type="checkbox" checked="checked" /< elem.checked true (Boolean) $(elem).prop("checked") true (Boolean) elem.getAttribute("checked") "checked" (String) $(elem).attr("checked")(1.6+) "checked" (String) $(elem).attr("checked")(pre-1.6) true (Boolean)
在 1.6 以前,attr 方法戴了两顶帽子:可以处理 attribute, 又可以处理 property. 带来的问题是,社区会误用 attr 方法。1.6 发布后,虽然导致了不兼容,但从长远来看,attr 方法如释重负,能更加关注做好本职工作了。
一个反例是 RequireJS 里,对 require 的“寄以厚望”,导致 require 方法一会是动态加载,一会是静态加载,一会又是获取模块接口,戴了很多帽子,这着实很恼火。
适度灵活
做一件事情只有一种方法,但可以有多种方式。还拿 append 来举例:
var container = $('#container'); container.append(['<p>paragraph</p>', $('#some')]); // snip...
append 多个时,直接传入数组,这也是一种很自然的联想(和 Array.concat 逻辑一致)。适度灵活最难的是如何判断是适度而不是过犹不及。
Dan Webb 提到的以下几点很值得思考:
- 对语言本身和标准类库,用户已经熟悉一些约定,复用这些约定很重要。
- 借鉴流行类库的约定,看他们怎么做的。
- 选项太多,导致迷惑。
- 良好的默认配置,胜过千万选项。
- 不要让我再读文档。
- 记住:你永远不可能取悦所有人。
- 选项丰富不意味着灵活性好。
- 一切事物都可修改。
小结
不好意思,写得比较散。
总而言之,在设计 API 时,要多看猪跑,要多思多想多权衡。要深入了解用户的实际使用场景,同时又要有勇气带领用户去不曾到过到但去了就不再想回来的地方。