浅谈关于JavaScript API设计的一些建议和准则

设计是一个很普遍的概念,一般是可以理解为为即将做的某件事先形成一个计划或框架。 (牛津英语词典)中,设计是一种将艺术,体系,硬件或者更多的东西编织到一块的主线。软件设计,特别是作为软件设计的次类的API设计,也是一样的。但是API设计常常很少关注软件发展,因为为其他程序员写代码的重要性要次于应用UI设计和最终用户体验。

但是API设计,作为我们自己写的库中提供的公共接口,能够向调用我们代码的开发者表现出我们库的一些特点和功能,所以API设计和UI设计一样重要。事实上,两者都是为应用可以提供更好的用户体验具有基本的方式。应用UI在用户UX中占有很重要的位置,应用API是开发者的UX。因此,应用API设计应该被给予和我们提供给用户的接口相同水平的考虑和关注。正像我们关注UI的功效,简洁性和优美,我们也应该同样的评估API的功效,简洁性和代码的优美性!

API设计——javascript API设计的内容,呈现了唯一的挑战对所有的开发者,不管是否你正在开发一个公共的库或者一个内部的库。javascript的动态性,库使用者的匿名和需求的模棱两可都给API设计者呈现了一个令人畏惧的挑战。然而对于一个好的API设计是没有捷径的,但是可以从现代流行的一些javascript库中提取出一些设计准则是可能的!

API设计: 天使和恶魔的斗争

javascript API中差的设计会给使用你API的开发者和你带来高的花费。差的设计会导致浪费,使用你API的开发者会因为设法搞弄明白你接口而浪费时间,而API的开发者会因为处理不断增加的需求和解决使用者的困惑而浪费时间。然而几乎所有的API当初被开发的时候,都是为了能够提取相同的功能,方便调用并节约时间。但设计不好的API会使你的库使用者和你产生疑惑,这些库真的能节约时间吗?

优秀的API设计,一方面,完成了提取的目标,同时也实现了自我描述。当一个API被良好的设计,使用者可以快速地和直观地完成工作,完全不用不停的使用文档或者持续的访问支持或者解答网站。你也可以通过封装一些开发者需要自己花大量时间开发的一些特征来节约库开发者的时间。好的设计不仅节约开发者的时间,可以使他们看起来更加聪明和有责任。同样帮助你的用户看起来聪明和能干也会使你看起来更加的牛逼!

对于javascript来说,API设计特别重要

不管什么编程语言或者框架,API设计是重要的,API设计的重要性对于javascript来说是高于其它许多语言的。首先,作为一个动态的和后期绑定的语言,javascript没有编译器可以实现一个安全网或者检测单元功能,所以javascript不可以发现你代码中的错误。Linting或检验框架 如 JSLintJSHint可以帮助我们。这些框架的功能可以指出javascript中的一些普遍的错误,但是当我们使用API时,他们却不能发现javascript的错误。

这一切都取决于你,你可以开发一个具有良好设计的API,这个API可以帮助你的用户掉进众所周知的“成功坑”,这就意味着你的库对于开发者来说是舒服的和熟悉的,同时也提供了积极的强化和当开发者和你的代码交互时建立的信心。

“掉进成功的坑里”最好的例子是jQuery 通过CSS选择器语法获取DOM元素的运用。例如,如果我想要获取所有带有类名的article元素,我可以运用jQuery这样做:

$("article.blogPost").fadeIn();

选择器article.blogPost和下面展现使用完全一样的语法,这绝不是偶然的!

article.blogPost {
 border-radius: 10px;
 background-color: salmon;
 box-shadow: 0px 0px 10px 2px #ccc;
}

jQuery的选择器引擎被设计为了使我和其他开发者能够使我对CSS选择器的理解和它的引擎进行交互。结果可想而知,如果jQuery需要我用一种新的,为特定目的形成的语法,我将失去快速,明显和高效。

我们可以获得灵感从这些框架中,如jQuery,或者其他框架,并应用这些灵感到我们的设计中。然而,获得灵感并不是抄袭,有个度的问题,任何设计过API的人如果是仅仅的基于别人的想法,不管好与坏,他都将继承。如果我们将在好的javascript中获得的准则运用到其他领域中,我们能开发拥有好的API的框架,这些API设计能被运用在任何情况下。

出色的Javascript APIs设计秘诀

虽然软件不具有与绘画或建筑类似的视觉评价标准,我们仍倾向于使用与物理实体一样的形容词来描述软件质量。例如,使用“优雅的”与“漂亮的”来赞美软件并不罕见。如果用与物理实体相似的形容词描述软件接口是合理的话,那么当然也可以使用与之相同的原则来评价软件设计。

在本节,将四个来自艺术领域的流行设计原则扩展至API设计中:

  1. 和谐一致
  2. 平衡
  3. 对称
  4. 重点突出

对每一个原则,将列出一到多个实例来说明,这些例子表明流行的Javascript库API设计是怎样遵循这些原则的。

原则1:一致性&协调性

在艺术作品中,一致性是一个作品背后不可缺少的观念,或者说设计者如何把一些事物组成连贯的一个整体。协调性,从另一方面来说,是一个作品相似元素的布局,这会在考虑整体时产生一种简洁的感觉。

对于API的设计者,这些原则可以通过在类库使用类似的和(或者)统一的元素来实现。就拿Kendo UI来说吧,一个创建富web应用程序的javascript框架。Kendo UI提供了一系列的UI控件和工具,这些都可以通过一个简单的语法初始化。比如,如果我想从一个无序列表创建一个树形控件(TreeView),我只需调用以下方法:

$("ul.tree").kendoTreeView({ /* Configuration goes here */ });

Kendo UI树形组件

如果我想通过一个列表创建一个面板PanelBar,我只需稍微改成不同的调用方法.

$("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的完整支持,也可以自定义我需要的功能:

var Book = Backbone.Model.extend({
 initialize: function() { ... },
 author: function() { ... },
 pubDate: function() { ... },
});

统一和协调的目的是让API新手感觉熟悉和舒服。通过虽然功能不同,但是语法相同或相似,使API变得熟悉,大大减轻了开发者使用新工具的负担。

原则 2 :平衡

下一条原则是平衡,组织元素时不会让某个部分过于重量级而盖过其它部分,使用时不稳定。艺术作品里,平衡就是视觉权重。即使不对称,作品中仍能感觉到不对称下的平衡,因为它遵循某种模式。上下文中的API设计的平衡,我特指代码的视觉权重和可预测性(看得出功能)。

平衡的API让人觉得其组成部分属于彼此,他们行为相同,或互补地完成一个目标。通过扩展,APIs也可以感觉平衡,它们允许开发人员简单的预测其他API并使用。如Modernizr的属性测试,它们的平衡性在两个方面,a)属性名对应HTML5和CSS术语和API名称,b)每个属性测试统一地返回true或false值。

// 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选择器引擎:

$("#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快速的把类库应用到生产.。

原则 3: 相称性

接下来的原则是相称性,它是用来衡量一个作品中元素的大小和数量的。与其说一个好的API是一个小的api,相称性是相对于用途的大小。一个相称的API它的API表面和它的能力范围相匹配。

例如,Moment.js,一个流行的日期转换和格式化类库,可以把它视为具有相称性,因为它的API表层是紧凑的,它和类库的目的明确的匹配。Moment.js用于处理日期,它的API提供了便利的功能用来处理javascript Date对象:

moment().format('dddd');
moment().startOf('hour').fromNow();

对于一个有针对性的类库,像Moment.js,保持API的专注和简单是非常重要的。对于更大和更广阔的类库,API的大小应当能够反映出类库自身的能力。

Underscore来说,作为一个多种用途功效的库,它提供大量便利的函数,这些被设计的函数是用来帮助开发者处理javascript集合,数组,函数和对象。它的API量远远超过像Moment.js这样的库,但是Underscore也是成比例的,因为库中每个函数都有自己的功效目的。考虑下面的例子,前两个例子用Underscore来处理数组,最后一个来处理字符串。

_.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, 它能够被用来查询和处理远程数据。

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的函数的添加合理化,你需要做的改变是松开皮带并且让库呼吸。你的用户在调用一个新的可以自我描述名字的函数时,将会更加节省时间,并且不会给另一个已经存在的方法添加负担。

原则 4: 强调性

在艺术作品中,强调是利用对比来使作品中某一方面脱颖而出形成一个焦点。在许多API中,焦点可能是一个通道或者类库主要方法的锚点。另外一个关于强调性的例子可以参考“链接”方式或者fluent API,它通过增加强调性效果突出了类库中心对象。jquery倾向于从许多功能演示中的强调这个对象:

$('ul.first').find('.overdue')
 .css('background-color','red')
 .end()
 .find('.due-soon')
 .css('background-color', 'yellow');

对于许多现代的类库,另一个关于强调的例子是可扩展性:类库创建者没有提供的那部分,会为你提供一个工具你可以自己完成相关扩展。

一个典型的例子可以参考jQuery'sfn(pronounced “effin”) namespace, 一般的扩展点可以通过数不清的插件和补充的类库来完成:

(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”的函数,我们已经在本文中看到过:

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之类的类库将变得非常复杂,甚至不可能实现。

API 设计:不只为库代码编写者

如果你不是一位 JavaScript 库的编写者,而是一位 JavaScript 应用开发者或库的实现者,你可能会认为本文中的原则并不适用于你。毕竟,我们大多数人在听到“API”的时候,往往想到的是第三方库,一如我在本文中的示例一样

事实是,API ,如同其定义所言,无非就是一个提供给他人利用的隔离功能的接口。 现在,让我用一句老话来强调很重要的一点:编写模块化的 JS 代码是为了实用,使用的次数并不重要。

正如本文中引用的类库,你可以把自己的javascript代码公开接口给其他人。即使你的代码的用户是一小部分或内部团队——甚至你构建自己的一个私有类库——你不必像一个公开类库的作者那样考虑本文中API设计原则和这些原则的实现。利用API设计的好处是即使只针对一个使用者,也要像对数百万使用者一样设计。

因为API的设计代表着对开发者的用户体验,它就像UI设计对于最终用户的重要性一样。正如我们可以通过学习一些原则和参考一些好的或者坏的例子来开发出优秀的UI一样,我们也可以通过同样的方式学习到更好的API设计。应用文中提到的四个原则,以及其他你自己发现的原则,可以帮助你建立出优秀的API,并且让用户得到良好的体验。

(0)

相关推荐

  • PhantomJS快速入门教程(服务器端的 JavaScript API 的 WebKit)

    PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API.它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG. PhantomJS 可以用于 页面自动化 , 网络监测 , 网页截屏 ,以及 无界面测试 等. PhantomJs官网:http://phantomjs.org/ GitHub:https://github.com/ariya/phantomjs/wiki/Quick

  • 深入浅析JavaScript的API设计原则

    一.接口的流畅性 好的接口是流畅易懂的,他主要体现如下几个方面: 1.简单 操作某个元素的css属性,下面是原生的方法: document.querySelectorAll('#id').style.color = 'red'; 封装之后 function a(selector, color) { document.querySelectorAll(selector)[].style.color = color } a('#a', 'red'); 从几十个字母长长的一行到简简单单的一个函数调用,

  • 微信WeixinJSBridge API使用实例

    注意:请在微信中测试 <!DOCTYPE html> <html> <head> <title>微信WeixinJSBridge API</title> <meta charset="utf-8" /> <script type="text/javascript"> (function(){ var a=document.getElementsByTagName("html

  • Java通过JsApi方式实现微信支付

    要使用JsApi进行微信支付,首先要从微信获得一个prepay_id,然后通过调用微信的jsapi完成支付,JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回.由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分. 示例代码如下: function onBridgeReady(){ WeixinJSBridge

  • javascript实现手机震动API代码

    现代浏览器里提供的新的API越来越倾向于移动手机应用,而不是传统的桌面应用,比如 javascript地理位置信息API .另外一个只针对手机应用的JavaScript API就是 振动(Vibration) API .很明显,这个API就是允许mobile程序员使用JavaScript调用手机的振动功能,并且能设定振动的方式和时长. 判断浏览器对振动API的支持情况 一个好的习惯就是在使用之前要检查一下当前你的应用环境.浏览器是否支持振动API.下面就是检测的方法: 复制代码 代码如下: //

  • JavaScript手机振动API

    很明显,这个API就是允许mobile程序员使用JavaScript调用手机的振动功能,并且能设定振动的方式和时长. 判断浏览器对振动API的支持情况 一个好的习惯就是在使用之前要检查一下当前你的应用环境.浏览器是否支持振动API.下面就是检测的方法: // Standards ftw! var supportsVibrate = "vibrate" in navigator; 在window.navigator对象里就只有一个关于振动的API:vibrate. 振动API基础应用 这

  • 浅谈关于JavaScript API设计的一些建议和准则

    设计是一个很普遍的概念,一般是可以理解为为即将做的某件事先形成一个计划或框架. (牛津英语词典)中,设计是一种将艺术,体系,硬件或者更多的东西编织到一块的主线.软件设计,特别是作为软件设计的次类的API设计,也是一样的.但是API设计常常很少关注软件发展,因为为其他程序员写代码的重要性要次于应用UI设计和最终用户体验. 但是API设计,作为我们自己写的库中提供的公共接口,能够向调用我们代码的开发者表现出我们库的一些特点和功能,所以API设计和UI设计一样重要.事实上,两者都是为应用可以提供更好的

  • 浅谈鸿蒙 JavaScript GUI 技术栈

    作者:doodlewind 链接:https://juejin.im/post/6872154561574862855 众所周知,刚刚开源的「鸿蒙 2.0」以 JavaScript 作为 IoT 应用开发的框架语言.这标志着继 SpaceX 上天之后,JavaScript 再一次蹭到了新闻联播级的热点.这么好的机会,只拿来阴阳怪气实在太可惜了.作为科普,这篇文章不会拿着放大镜找出代码中的槽点来吹毛求疵,而是希望通俗地讲清楚它所支持的 GUI 到底是怎么一回事.只要对计算机基础有个大概的了解,应该

  • 浅谈Vue3 Composition API如何替换Vue Mixins

    想在你的Vue组件之间共享代码?如果你熟悉Vue 2 则可能知道使用mixin,但是新的Composition API 提供了更好的解决方案. 在本文中,我们将研究mixins的缺点,并了解Composition API如何克服它们,并使Vue应用程序具有更大的可伸缩性. 回顾Mixins功能 让我们快速回顾一下mixins模式,因为对于下一部分我们将要讲到的内容,请务必将其放在首位. 通常,Vue组件是由一个JavaScript对象定义的,它具有表示我们所需功能的各种属性--诸如 data,m

  • 浅谈Web Storage API的使用

    目录 一.浏览器的本地存储技术 1.1.sessionStorage 1.2.localStorage 二.Web Storage相关接口 三.浏览器兼容性 四.隐身模式 五.使用Web Storage API 一.浏览器的本地存储技术 除了最早的使用cookie来进行本地存储之外,现代浏览器使用Web Storage API来方便的进行key/value的存储. Web Storage有两种存储方式: 1.1.sessionStorage 对于每一个访问源,都会维持一个独立的存储区域.只要浏览

  • 浅谈Tomcat多层容器的设计

    目录 容器的层次结构 请求定位Servlet的过程 工作原理 Tomcat的容器用来装载Servlet.那Tomcat的Servlet容器是如何设计的呢? 容器的层次结构 Tomcat设计了4种容器:Engine.Host.Context和Wrapper Tomcat通过这种分层,使得Servlet容器具有很好的灵活性. Context表示一个Web应用程序 Wrapper表示一个Servlet,一个Web应用程序中可能会有多个Servlet Host代表一个虚拟主机,或一个站点,可以给Tomc

  • 浅谈Spark RDD API中的Map和Reduce

    RDD是什么? RDD是Spark中的抽象数据结构类型,任何数据在Spark中都被表示为RDD.从编程的角度来看,RDD可以简单看成是一个数组.和普通数组的区别是,RDD中的数据是分区存储的,这样不同分区的数据就可以分布在不同的机器上,同时可以被并行处理.因此,Spark应用程序所做的无非是把需要处理的数据转换为RDD,然后对RDD进行一系列的变换和操作从而得到结果.本文为第一部分,将介绍Spark RDD中与Map和Reduce相关的API中. 如何创建RDD? RDD可以从普通数组创建出来,

  • 浅谈mysql的索引设计原则以及常见索引的区别

    索引定义:是一个单独的,存储在磁盘上的数据库结构,其包含着对数据表里所有记录的引用指针. 数据库索引的设计原则: 为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引. 那么索引设计原则又是怎样的? 1.选择唯一性索引 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录. 例如,学生表中学号是具有唯一性的字段.为该字段建立唯一性索引可以很快的确定某个学生的信息. 如果使用姓名的话,可能存在同名现象,从而降低查询速度. 2.为经常需要排序.分组和联合操

  • 浅谈Python处理json字符串为什么不建议使用eval()

    目录 一.前言 二.Json.loads与eval 性能对比 1. eval 2. json.loads 一.前言 最近发现一些小伙伴使用eval来处理json,而且为了能够将json成功转为字典而不报错,还写了如下的赋值操作 (因为json中空为null,假为false,真为true与Python的表达不一样,如果不进行下面代码的赋值,用eval转换就会报错): null=None false=False true=True 其实Python的标准库中有处理json的库,就叫json,比如要把

  • 浅谈关于JavaScript的语言特性分析

    前言在JavaScript中,作用域.上下文.闭包.函数等算是精华中的精华了.对于初级JSer来说,是进阶必备.对于前端攻城师来说,只有静下心来,理解了这些精华,才能写出优雅的代码. 本文旨在总结容易忘记的重要知识,不会讲基本的概念.如果对基本知识不太熟悉,就去翻下< JavaScript权威指南>吧~ 语言特性函数表达式 先看代码段: 复制代码 代码如下: [javascript] view plaincopyprint?var f = function foo(){      return

  • 浅谈利用JavaScript进行的DDoS攻击原理与防御

    分布式拒绝服务攻击(DDoS)攻击是一种针对网站发起的最古老最普遍的攻击.Nick Sullivan是网站加速和安全服务提供商CloudFlare的一名系统工程师.近日,他撰文介绍了攻击者如何利用恶意网站.服务器劫持和中间人攻击发起DDoS攻击,并说明了如何使用HTTPS以及即将到来的名为"子资源一致性(Subresource Integrity,简称SRI)"的Web新技术保护网站免受攻击. 现代网站的大部分交互都来自于JavaScript.网站通过直接向HTML中添加JavaScr

随机推荐