デザインは非常に一般的な概念であり、一般に、何かを行うための計画または枠組みを形成するものとして理解できます。 (オックスフォード英語辞典)、デザインとは、アート、システム、ハードウェアなどを織り交ぜた糸です。ソフトウェア設計、特にソフトウェア設計のサブカテゴリとしての API 設計も同様です。しかし、他のプログラマーのためにコードを書くことは、アプリケーションの UI 設計やエンドユーザー エクスペリエンスよりも重要であるため、API 設計ではソフトウェア開発にはほとんど注意が払われないことがよくあります。
しかし、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 が適切に設計されていれば、ユーザーは常にドキュメントを参照したり、サポート Web サイトにアクセスしたり、Web サイトに回答したりすることなく、作業を迅速かつ直感的に行うことができます。また、ライブラリ開発者が自分で開発するには多大な時間がかかるいくつかの機能をカプセル化することで、ライブラリ開発者の時間を節約することもできます。優れたデザインは開発者の時間を節約するだけでなく、開発者をより賢く、より責任感のあるものに見せます。また、ユーザーが賢くて有能に見えるようにすることで、あなたがさらに素晴らしく見えるようになります。
JavaScript では API 設計が特に重要です
どのようなプログラミング言語やフレームワークであっても、JavaScript の API 設計の重要性は他の多くの言語よりも高くなります。まず、動的かつ遅延バインディング言語である JavaScript には、セーフティ ネットや検出ユニット機能を実装できるコンパイラがないため、JavaScript はコード内のエラーを見つけることができません。 Lint や、JSLint や JSHint などの検証フレームワークが役立ちます。これらのフレームワークの機能は、JavaScript の一般的なエラーを指摘できますが、API を使用する場合は JavaScript エラーを見つけることができません。
すべてはあなた次第です。ユーザーがことわざの「成功の落とし穴」に陥るのを助ける、適切に設計された API を開発できるかどうかは、あなたのライブラリが開発者にとって快適で親しみやすいものであると同時に、ポジティブな補強を提供し、自信を築くことができることを意味します。開発者はコードを操作します。
「成功の落とし穴に陥る」最良の例は、jQuery を使用して CSS セレクター構文を通じて DOM 要素を取得することです。たとえば、クラス名を持つすべての記事要素を取得したい場合は、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设计中:
对每一个原则,将列出一到多个实例来说明,这些例子表明流行的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() { ... } });
拡張性は、既存のクラス ライブラリがすべて完璧であることを意味するわけではないという事実に気づかせてくれるため、また、自分に合ったクラス ライブラリを拡張するよう促すため、重要な側面です。クラス ライブラリが拡張機能をサポートすると、新しい用途が広がるだけでなく、無数の開発者が一般的な用途から恩恵を受けることも可能になります。最良の例の 1 つは Backbone.Marionette フレームワークです。これは Backbone を拡張するクラス ライブラリであり、その目標は「大規模な JavaScript アプリケーションの構造を簡素化する」ことです。 Backbone のようなライブラリ拡張がなければ、Marionette のようなライブラリは非常に複雑になるか、実装が不可能になる可能性があります。
API 設計: ライブラリ コード作成者だけではない
あなたが JavaScript ライブラリ作成者ではなく、JavaScript アプリケーション開発者またはライブラリ実装者である場合、この記事の原則は当てはまらないと感じるかもしれません。結局のところ、ほとんどの人は「API」と聞くと、この記事で使用した例のように、サードパーティのライブラリを思い浮かべる傾向があります
実際には、その定義にあるように、API は他の人が利用できるように分離された機能を提供するインターフェースにすぎません。 ここで、重要な点を強調するために古いことわざを使用しましょう。実用性を重視してモジュール式の JS コードを作成し、使用される回数は問題ではありません。
この記事で参照されているライブラリと同様に、JavaScript コードを他の人に公開できます。コードのユーザーが小さなグループや内部チームである場合でも、独自のプライベート ライブラリを構築している場合でも、この記事の API 設計原則と、それらの原則の実装について考える必要はありません。公共図書館の著者がそうする方法です。 API 設計を活用する利点は、たとえ 1 人のユーザーのみを対象とする場合でも、数百万人のユーザーを対象とするかのように設計する必要があることです。
API デザインは開発者にとってのユーザー エクスペリエンスを表すため、エンド ユーザーにとっては UI デザインと同じくらい重要です。いくつかの原則を学び、良い例と悪い例を参照することで良い UI を開発できるのと同じように、より良い API 設計を学ぶこともできます。この記事で説明した 4 つの原則と、自分で見つけた他の原則を適用すると、優れた API を構築し、ユーザーに優れたエクスペリエンスを提供することができます。