出色的 JavaScript API 設計祕訣

設計是一個很廣泛的概念,通常是能夠理解爲爲即將作的某件事先造成一個計劃或框架。 (牛津英語詞典)中,設計是一種將藝術,體系,硬件或者更多的東西編織到一塊的主線。軟件設計,特別是做爲軟件設計的次類的API設計,也是同樣的。可是API設計經常不多關注軟件發展,由於爲其餘程序員寫代碼的重要性要次於應用UI設計和最終用戶體驗。javascript

可是API設計,做爲咱們本身寫的庫中提供的公共接口,可以向調用咱們代碼的開發者表現出咱們庫的一些特色和功能,因此API設計和UI設計同樣重要。事實上,二者都是爲應用能夠提供更好的用戶體驗具備基本的方式。應用UI在用戶UX中佔有很重要的位置,應用API是開發者的UX。所以,應用API設計應該被給予和咱們提供給用戶的接口相同水平的考慮和關注。正像咱們關注UI的功效,簡潔性和優美,咱們也應該一樣的評估API的功效,簡潔性和代碼的優美性!css

API設計——javascript API設計的內容,呈現了惟一的挑戰對全部的開發者,無論是否你正在開發一個公共的庫或者一個內部的庫。javascript的動態性,庫使用者的匿名和需求的模棱兩可都給API設計者呈現了一個使人畏懼的挑戰。然而對於一個好的API設計是沒有捷徑的,可是能夠從現代流行的一些javascript庫中提取出一些設計準則是可能的!html

API設計: 天使和惡魔的鬥爭

javascript API中差的設計會給使用你API的開發者和你帶來高的花費。差的設計會致使浪費,使用你API的開發者會由於設法搞弄明白你接口而浪費時間,而API的開發者會由於處理不斷增長的需求和解決使用者的困惑而浪費時間。然而幾乎全部的API當初被開發的時候,都是爲了可以提取相同的功能,方便調用並節約時間。但設計很差的API會使你的庫使用者和你產生疑惑,這些庫真的能節約時間嗎?java

優秀的API設計,一方面,完成了提取的目標,同時也實現了自我描述。當一個API被良好的設計,使用者能夠快速地和直觀地完成工做,徹底不用不停的使用文檔或者持續的訪問支持或者解答網站。你也能夠經過封裝一些開發者須要本身花大量時間開發的一些特徵來節約庫開發者的時間。好的設計不只節約開發者的時間,可使他們看起來更加聰明和有責任。一樣幫助你的用戶看起來聰明和能幹也會使你看起來更加的牛逼!jquery

對於javascript來講,API設計特別重要

無論什麼編程語言或者框架,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設計能被運用在任何狀況下。

出色的Javascript APIs設計祕訣

雖然軟件不具備與繪畫或建築相似的視覺評價標準,咱們仍傾向於使用與物理實體同樣的形容詞來描述軟件質量。例如,使用「優雅的」與「漂亮的」來讚美軟件並不罕見。若是用與物理實體類似的形容詞描述軟件接口是合理的話,那麼固然也可使用與之相同的原則來評價軟件設計。

在本節,將四個來自藝術領域的流行設計原則擴展至API設計中:

  • 和諧一致
  • 平衡
  • 對稱
  • 重點突出

對每個原則,將列出一到多個實例來講明,這些例子代表流行的Javascript庫API設計是怎樣遵循這些原則的。

原則1:一致性&協調性

在藝術做品中,一致性是一個做品背後不可缺乏的觀念,或者說設計者如何把一些事物組成連貫的一個總體。協調性,從另外一方面來講,是一個做品類似元素的佈局,這會在考慮總體時產生一種簡潔的感受。

對於API的設計者,這些原則能夠經過在類庫使用相似的和(或者)統一的元素來實現。就拿Kendo UI來講吧,一個建立富web應用程序的javascript框架。Kendo UI提供了一系列的UI控件和工具,這些均可以經過一個簡單的語法初始化。好比,若是我想從一個無序列表建立一個樹形控件(TreeView),我只需調用如下方法:

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

出色的 JavaScript API 設計祕訣

Kendo UI樹形組件

若是我想經過一個列表建立一個面板PanelBar,我只需稍微改爲不一樣的調用方法.

1
$( "ul.panel" ).kendoPanelBar({ /* Configuration goes here */ });

出色的 JavaScript API 設計祕訣

Kendo UI 面板組件

Kendo UI 對全部組件使用一致的kendoX語法,促進總體的協調。更重要的,這樣的設計依賴jQuery對象爲DOM元素封裝了統一的一層,使設計有利於全部熟悉jQuery開發者。數百萬開發者使用相似的「土語」(jQuery語法),Kendo UI能夠順利地跨庫使用。

另外一個協調的案例是Backbone的[object].extend語法建立對象,繼承和擴展Backbone的Models,Views,CollectionsRouters的功能。用以下代碼就能夠建立一個Backbone Model,帶有Backbone的完整支持,也能夠自定義我須要的功能:

1
2
3
4
5
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值。

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

出色的 JavaScript API 設計祕訣

訪問一個單一的屬性來告訴開發者須要瞭解到的相關屬性,以便經過它訪問每個其餘屬性,一個高質量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快速的把類庫應用到生產。

原則 3: 相稱性

接下來的原則是相稱性,它是用來衡量一個做品中元素的大小和數量的。與其說一個好的API是一個小的api,相稱性是相對於用途的大小。一個相稱的API它的API表面和它的能力範圍相匹配。

例如,Moment.js,一個流行的日期轉換和格式化類庫,能夠把它視爲具備相稱性,由於它的API表層是緊湊的,它和類庫的目的明確的匹配。Moment.js用於處理日期,它的API提供了便利的功能用來處理javascript Date對象:

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

出色的 JavaScript API 設計祕訣

對於一個有針對性的類庫,像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

出色的 JavaScript API 設計祕訣

當一個庫逐漸成長的過程當中,維持比例的挑戰變的更加具備嚴峻。爲了確保添加進庫的每一個功能和函數都能增強庫的目的,須要更多的考慮投入。對於一個大的庫像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的函數的添加合理化,你須要作的改變是鬆開皮帶而且讓庫呼吸。你的用戶在調用一個新的能夠自我描述名字的函數時,將會更加節省時間,而且不會給另外一個已經存在的方法添加負擔。

原則 4: 強調性

在藝術做品中,強調是利用對比來使做品中某一方面脫穎而出造成一個焦點。在許多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 () { ... }
});

出色的 JavaScript API 設計祕訣

可擴展做爲強調性的一方面是由於它讓咱們意識到這樣的一個事實,已有的類庫並不意味着一切是完美的,同時也鼓勵咱們擴展適合本身的類庫。當類庫支持擴展時,它們不只開啓了新的用途,也使無數開發者受益於通常的用途。一個最好的例子是Backbone.Marionette框架,一個擴展於Backbone的類庫,它的目標是「簡化大型的javascript應用程序的結構」。若是不是像Backbone那樣的類庫擴展,Marionette之類的類庫將變得很是複雜,甚至不可能實現。

API 設計:不僅爲庫代碼編寫者

若是你不是一位 JavaScript 庫的編寫者,而是一位 JavaScript 應用開發者或庫的實現者,你可能會認爲本文中的原則並不適用於你。畢竟,咱們大多數人在聽到「API」的時候,每每想到的是第三方庫,一如我在本文中的示例同樣

事實是,API ,如同其定義所言,無非就是一個提供給他人利用的隔離功能的接口。 如今,讓我用一句老話來強調很重要的一點:編寫模塊化的 JS 代碼是爲了實用,使用的次數並不重要。

正如本文中引用的類庫,你能夠把本身的javascript代碼公開接口給其餘人。即便你的代碼的用戶是一小部分或內部團隊——甚至你構建本身的一個私有類庫——你沒必要像一個公開類庫的做者那樣考慮本文中API設計原則和這些原則的實現。利用API設計的好處是即便只針對一個使用者,也要像對數百萬使用者同樣設計。

由於API的設計表明着對開發者的用戶體驗,它就像UI設計對於最終用戶的重要性同樣。正如咱們能夠經過學習一些原則和參考一些好的或者壞的例子來開發出優秀的UI同樣,咱們也能夠經過一樣的方式學習到更好的API設計。應用文中提到的四個原則,以及其餘你本身發現的原則,能夠幫助你創建出優秀的API,而且讓用戶獲得良好的體驗。

相關文章
相關標籤/搜索