岁月如歌

用开放的心态,打造专业的人生。

API 设计二三事

with 4 comments

所有程序员都是 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 有个很重要的非兼容性变化

&gt;input type="checkbox" checked="checked" /&lt;
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 时,要多看猪跑,要多思多想多权衡。要深入了解用户的实际使用场景,同时又要有勇气带领用户去不曾到过到但去了就不再想回来的地方。

Advertisements

Written by lifesinger

May 9, 2011 at 12:34

Posted in Articles

4 Responses

Subscribe to comments with RSS.

  1. $.when()就不支持数组,比较纠结~

    skyblue

    May 11, 2011 at 13:14

  2. […] https://lifesinger.wordpress.com/2011/05/09/api-design/ 此条目发表在 未分类 分类目录,贴了 api, Javascript, jquery, Programming, seajs […]

  3. Dan Webb的那几点超赞!!
    框架和库的用户是程序员,用户体验好的框架才是真正的好框架

    草依山

    May 17, 2011 at 23:34


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s