设计是一个很广泛的概念,通常是能够理解为为即将作的某件事先造成一个计划或框架。 (牛津英语词典)中,设计是一种将艺术,体系,硬件或者更多的东西编织到一块的主线。软件设计,特别是做为软件设计的次类的API设计,也是同样的。可是API设计经常不多关注软件发展,由于为其余程序员写代码的重要性要次于应用UI设计和最终用户体验。javascript
可是API设计,做为咱们本身写的库中提供的公共接口,可以向调用咱们代码的开发者表现出咱们库的一些特色和功能,因此API设计和UI设计同样重要。事实上,二者都是为应用能够提供更好的用户体验具备基本的方式。应用UI在用户UX中占有很重要的位置,应用API是开发者的UX。所以,应用API设计应该被给予和咱们提供给用户的接口相同水平的考虑和关注。正像咱们关注UI的功效,简洁性和优美,咱们也应该一样的评估API的功效,简洁性和代码的优美性!css
API设计——javascript API设计的内容,呈现了惟一的挑战对全部的开发者,无论是否你正在开发一个公共的库或者一个内部的库。javascript的动态性,库使用者的匿名和需求的模棱两可都给API设计者呈现了一个使人畏惧的挑战。然而对于一个好的API设计是没有捷径的,可是能够从现代流行的一些javascript库中提取出一些设计准则是可能的!html
javascript API中差的设计会给使用你API的开发者和你带来高的花费。差的设计会致使浪费,使用你API的开发者会由于设法搞弄明白你接口而浪费时间,而API的开发者会由于处理不断增长的需求和解决使用者的困惑而浪费时间。然而几乎全部的API当初被开发的时候,都是为了可以提取相同的功能,方便调用并节约时间。但设计很差的API会使你的库使用者和你产生疑惑,这些库真的能节约时间吗?java
优秀的API设计,一方面,完成了提取的目标,同时也实现了自我描述。当一个API被良好的设计,使用者能够快速地和直观地完成工做,彻底不用不停的使用文档或者持续的访问支持或者解答网站。你也能够经过封装一些开发者须要本身花大量时间开发的一些特征来节约库开发者的时间。好的设计不只节约开发者的时间,可使他们看起来更加聪明和有责任。一样帮助你的用户看起来聪明和能干也会使你看起来更加的牛逼!jquery
无论什么编程语言或者框架,API设计是重要的,API设计的重要性对于javascript来讲是高于其它许多语言的。首先,做为一个动态的和后期绑定的语言,javascript没有编译器能够实现一个安全网或者检测单元功能,因此javascript不能够发现你代码中的错误。Linting 或检验框架 如 JSLint 和JSHint 能够帮助咱们。这些框架的功能能够指出javascript中的一些广泛的错误,可是当咱们使用API时,他们却不能发现javascript的错误。程序员
这一切都取决于你,你能够开发一个具备良好设计的API,这个API能够帮助你的用户掉进众所周知的“成功坑”,这就意味着你的库对于开发者来讲是舒服的和熟悉的,同时也提供了积极的强化和当开发者和你的代码交互时创建的信心。web
“掉进成功的坑里”最好的例子是jQuery 经过CSS选择器语法获取DOM元素的运用。例如,若是我想要获取全部带有类名的article元素,我能够运用jQuery这样作:编程
1
|
$(
"article.blogPost"
).fadeIn();
|
选择器article.blogPost和下面展示使用彻底同样的语法,这毫不是偶然的!json
1
2
3
4
5
|
article.blogPost {
border-radius: 10px;
background-color: salmon;
box-shadow: 0px 0px 10px 2px
#ccc;
}
|
jQuery的选择器引擎被设计为了使我和其余开发者可以使我对CSS选择器的理解和它的引擎进行交互。结果可想而知,若是jQuery须要我用一种新的,为特定目的造成的语法,我将失去快速,明显和高效。canvas
咱们能够得到灵感从这些框架中,如jQuery,或者其余框架,并应用这些灵感到咱们的设计中。然而,得到灵感并非抄袭,有个度的问题,任何设计过API的人若是是仅仅的基于别人的想法,无论好与坏,他都将继承。若是咱们将在好的javascript中得到的准则运用到其余领域中,咱们能开发拥有好的API的框架,这些API设计能被运用在任何状况下。
虽然软件不具备与绘画或建筑相似的视觉评价标准,咱们仍倾向于使用与物理实体同样的形容词来描述软件质量。例如,使用“优雅的”与“漂亮的”来赞美软件并不罕见。若是用与物理实体类似的形容词描述软件接口是合理的话,那么固然也可使用与之相同的原则来评价软件设计。
在本节,将四个来自艺术领域的流行设计原则扩展至API设计中:
对每个原则,将列出一到多个实例来讲明,这些例子代表流行的Javascript库API设计是怎样遵循这些原则的。
在艺术做品中,一致性是一个做品背后不可缺乏的观念,或者说设计者如何把一些事物组成连贯的一个总体。协调性,从另外一方面来讲,是一个做品类似元素的布局,这会在考虑总体时产生一种简洁的感受。
对于API的设计者,这些原则能够经过在类库使用相似的和(或者)统一的元素来实现。就拿Kendo UI来讲吧,一个建立富web应用程序的javascript框架。Kendo UI提供了一系列的UI控件和工具,这些均可以经过一个简单的语法初始化。好比,若是我想从一个无序列表建立一个树形控件(TreeView),我只需调用如下方法:
1
|
$(
"ul.tree"
).kendoTreeView({
/* Configuration goes here */
});
|
Kendo UI树形组件
若是我想经过一个列表建立一个面板PanelBar,我只需稍微改为不一样的调用方法.
1
|
$(
"ul.panel"
).kendoPanelBar({
/* Configuration goes here */
});
|
Kendo UI 面板组件
Kendo UI 对全部组件使用一致的kendoX语法,促进总体的协调。更重要的,这样的设计依赖jQuery对象为DOM元素封装了统一的一层,使设计有利于全部熟悉jQuery开发者。数百万开发者使用相似的“土语”(jQuery语法),Kendo UI能够顺利地跨库使用。
另外一个协调的案例是Backbone的[object].extend语法建立对象,继承和扩展Backbone的Models,Views,Collections和Routers的功能。用以下代码就能够建立一个Backbone Model,带有Backbone的完整支持,也能够自定义我须要的功能:
1
2
3
4
5
|
var
Book = Backbone.Model.extend({
initialize:
function
() { ... },
author:
function
() { ... },
pubDate:
function
() { ... },
});
|
统一和协调的目的是让API新手感受熟悉和舒服。经过虽然功能不一样,可是语法相同或类似,使API变得熟悉,大大减轻了开发者使用新工具的负担。
下一条原则是平衡,组织元素时不会让某个部分过于重量级而盖过其它部分,使用时不稳定。艺术做品里,平衡就是视觉权重。即便不对称,做品中仍能感受到不对称下的平衡,由于它遵循某种模式。上下文中的API设计的平衡,我特指代码的视觉权重和可预测性(看得出功能)。 平衡的API让人以为其组成部分属于彼此,他们行为相同,或互补地完成一个目标。经过扩展,APIs也能够感受平衡,它们容许开发人员简单的预测其余API并使用。如Modernizr的属性测试,它们的平衡性在两个方面,a)属性名对应HTML5和CSS术语和API名称,b)每一个属性测试统一地返回true或false值。
1
2
3
4
5
6
7
8
|
// All of these properties will be 'true' or 'false' for a given browser
Modernizr.geolocation
Modernizr.localstorage
Modernizr.webworkers
Modernizr.canvas
Modernizr.borderradius
Modernizr.boxshadow
Modernizr.flexbox
|
访问一个单一的属性来告诉开发者须要了解到的相关属性,以便经过它访问每个其余属性,一个高质量API的强大之处就在于它的简单。平衡性也保证了我写和Modernizr交互的代码在每次读写时具备相同的视觉加权。如何在我使用和访问API时看起来和感受上同样,而不顾个人惯例。另外一方面,若是Modernizr添加了一个polyfill Canvas的API,不只仅是类库的视觉加权受到新API的影响,Modernizr的范围和用途也将大大扩大,而且我在和API交互时可预测性也受到了限制。
达到平衡的另外一种方式是经过依靠开发人员对概念的熟悉得到可预测性的结果。一个典型的例子就是jQuery’s selector syntax(jquery选择器的语法),它映射css1-3的选择器到本身的DOM选择器引擎:
1
2
3
|
$(
"#grid"
)
// Selects by ID
$(
"ul.nav > li"
)
// All LIs for the UL with class "nav"
$(
"ul li:nth-child(2)"
)
// Second item in each list
|
经过使用一个熟悉的概念而且映射到本身的类库,jquery避免了新的选择器语法,同事也建立了一个机制让新用户经过一个可预测的API快速的把类库应用到生产。
接下来的原则是相称性,它是用来衡量一个做品中元素的大小和数量的。与其说一个好的API是一个小的api,相称性是相对于用途的大小。一个相称的API它的API表面和它的能力范围相匹配。
例如,Moment.js,一个流行的日期转换和格式化类库,能够把它视为具备相称性,由于它的API表层是紧凑的,它和类库的目的明确的匹配。Moment.js用于处理日期,它的API提供了便利的功能用来处理javascript Date对象:
1
2
|
moment().format(
'dddd'
);
moment().startOf(
'hour'
).fromNow();
|
对于一个有针对性的类库,像Moment.js,保持API的专一和简单是很是重要的。对于更大和更广阔的类库,API的大小应当可以反映出类库自身的能力。
拿Underscore来讲,做为一个多种用途功效的库,它提供大量便利的函数,这些被设计的函数是用来帮助开发者处理javascript集合,数组,函数和对象。它的API量远远超过像Moment.js这样的库,可是Underscore也是成比例的,由于库中每一个函数都有本身的功效目的。考虑下面的例子,前两个例子用Underscore来处理数组,最后一个来处理字符串。
1
2
3
4
5
6
7
8
9
|
_.each([
"Todd"
,
"Burke"
,
"Derick"
],
function
(name){
alert(name);
});
_.map([1, 2, 3],
function
(num){
return
num * 3;
});
_.isNumber(
"ten"
);
// False
|
当一个库逐渐成长的过程当中,维持比例的挑战变的更加具备严峻。为了确保添加进库的每一个功能和函数都能增强库的目的,须要更多的考虑投入。对于一个大的库像kendo UI,易扩展性的目的并非意味着咱们须要往库中添加每一个特性。对于一个像kendo同样大的库,功能对象和特性应该证实它们的价值才能被库包含。例如, Kendo UI’s JavaScript 基于DataSource, 它可以被用来查询和处理远程数据。
1
2
3
4
5
6
7
8
9
10
|
var
dataSource =
new
kendo.data.DataSource({
transport: {
read: {
url:
"http://search.twitter.com/search.json"
,
dataType:
"jsonp"
,
data: { q:
"API Design"
}
}
},
schema: { data:
"results"
}
});
|
初看第一眼,它好像一个习觉得常的数据源,感受超出了库自己的基本目的。然而今天网站的装饰都须要动态数据的支持。数据源的引入容许Kendo UI可使用一个稳定,并温馨的范式在整个库范围内来解决远程数据。
让一个API转变为一个名符其实的javascript垃圾抽屉,对于一个库的扩展这是危险的,但对于库来讲,这也不是惟一的危险。掉入一个不让你的API伴随着库的成长圈套,或者因为某些人为缘由,限制你库的大小,这些一样都是危险的!
不处理API增加最好的一个例子是jQuery的 jQuery or $ function。和我同样有成千上万的开发者喜欢jQurey, 但它的门户方法是有点乱的,从DOM选择到在jQuery对象中包含DOM元素,这个方法提供了超过11个独立超负荷选择方式。
就大部分而言,有些不是十分相关的特性被硬塞进同一个API。从全局看,jQuery是一个大的库而且能被认为库比例是合理的。另外一方面,当咱们尝试将一个功能硬塞进一个单一接口而且不考虑库比例,jQuery方法也能够实现这样的功能。
若是你发现你正在将一个不相干的特性强塞进已经存在的方法,或者正在想法设法使一个并不适合API的函数的添加合理化,你须要作的改变是松开皮带而且让库呼吸。你的用户在调用一个新的能够自我描述名字的函数时,将会更加节省时间,而且不会给另外一个已经存在的方法添加负担。
在艺术做品中,强调是利用对比来使做品中某一方面脱颖而出造成一个焦点。在许多API中,焦点多是一个通道或者类库主要方法的锚点。另一个关于强调性的例子能够参考“连接”方式或者fluent API,它经过增长强调性效果突出了类库中心对象。jquery倾向于从许多功能演示中的强调这个对象:
1
2
3
4
5
|
$(
'ul.first'
).find(
'.overdue'
)
.css(
'background-color'
,
'red'
)
.end()
.find(
'.due-soon'
)
.css(
'background-color'
,
'yellow'
);
|
对于许多现代的类库,另外一个关于强调的例子是可扩展性:类库建立者没有提供的那部分,会为你提供一个工具你能够本身完成相关扩展。
一个典型的例子能够参考 jQuery’sfn(pronounced “effin”) namespace, 通常的扩展点能够经过数不清的插件和补充的类库来完成:
1
2
3
4
5
6
7
8
9
10
|
(
function
($) {
$.fn.kittehfy =
function
() {
return
this
.each(
function
(idx, el) {
var
width = el.width,
height = el.height;
var
src=
"http://placekitten.com/"
;
el.src= src + width +
"/"
+ height;
});
};
})(jQuery);
|
另外一个扩展性的例子是Backbone的“extend”的函数,咱们已经在本文中看到过:
1
2
3
4
5
6
7
8
9
|
var
DocumentRow = Backbone.View.extend({
tagName:
"li"
,
className:
"row"
,
events: {
"click .icon"
:
"open"
,
"click .button.edit"
:
"openEditDialog"
},
render:
function
() { ... }
});
|
可扩展做为强调性的一方面是由于它让咱们意识到这样的一个事实,已有的类库并不意味着一切是完美的,同时也鼓励咱们扩展适合本身的类库。当类库支持扩展时,它们不只开启了新的用途,也使无数开发者受益于通常的用途。一个最好的例子是Backbone.Marionette框架,一个扩展于Backbone的类库,它的目标是“简化大型的javascript应用程序的结构”。若是不是像Backbone那样的类库扩展,Marionette之类的类库将变得很是复杂,甚至不可能实现。
若是你不是一位 JavaScript 库的编写者,而是一位 JavaScript 应用开发者或库的实现者,你可能会认为本文中的原则并不适用于你。毕竟,咱们大多数人在听到“API”的时候,每每想到的是第三方库,一如我在本文中的示例同样
事实是,API ,如同其定义所言,无非就是一个提供给他人利用的隔离功能的接口。 如今,让我用一句老话来强调很重要的一点:编写模块化的 JS 代码是为了实用,使用的次数并不重要。
正如本文中引用的类库,你能够把本身的javascript代码公开接口给其余人。即便你的代码的用户是一小部分或内部团队——甚至你构建本身的一个私有类库——你没必要像一个公开类库的做者那样考虑本文中API设计原则和这些原则的实现。利用API设计的好处是即便只针对一个使用者,也要像对数百万使用者同样设计。
由于API的设计表明着对开发者的用户体验,它就像UI设计对于最终用户的重要性同样。正如咱们能够经过学习一些原则和参考一些好的或者坏的例子来开发出优秀的UI同样,咱们也能够经过一样的方式学习到更好的API设计。应用文中提到的四个原则,以及其余你本身发现的原则,能够帮助你创建出优秀的API,而且让用户获得良好的体验。