디자인은 매우 일반적인 개념으로, 일반적으로 수행할 작업에 대한 계획이나 틀을 형성하는 것으로 이해될 수 있습니다. (Oxford English Dictionary)에 따르면 디자인은 예술, 시스템, 하드웨어 등을 하나로 엮는 실입니다. 소프트웨어 디자인, 특히 소프트웨어 디자인의 하위 카테고리인 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가 잘 설계되면 사용자는 지속적으로 문서를 찾아보거나 지원을 방문하거나 웹 사이트에 응답할 필요 없이 빠르고 직관적으로 작업을 완료할 수 있습니다. 또한 자체 개발하는 데 많은 시간이 걸리는 일부 기능을 캡슐화하여 라이브러리 개발자의 시간을 절약할 수 있습니다. 좋은 디자인은 개발자의 시간을 절약해 줄 뿐만 아니라 개발자를 더욱 똑똑하고 책임감 있게 보이게 만들어 줍니다. 또한 사용자가 똑똑하고 유능해 보이도록 도와주면 귀하도 더욱 멋져 보일 것입니다!
자바스크립트는 특히 API 디자인이 중요합니다
어떤 프로그래밍 언어나 프레임워크이든 API 디자인은 중요합니다. JavaScript에 대한 API 디자인의 중요성은 다른 많은 언어보다 높습니다. 첫째, 동적 및 런타임 바인딩 언어인 JavaScript에는 안전망이나 감지 단위 기능을 구현할 수 있는 컴파일러가 없으므로 JavaScript는 코드에서 오류를 찾을 수 없습니다. Lint 또는 JSLint 및 JSHint 와 같은 유효성 검사 프레임워크가 도움이 될 수 있습니다. 이러한 프레임워크의 기능은 자바스크립트의 몇 가지 일반적인 오류를 지적할 수 있지만 API를 사용할 때 자바스크립트 오류를 찾을 수는 없습니다.
모두 당신에게 달려 있습니다. 사용자가 속담의 "성공의 구렁텅이"에 빠지도록 돕는 잘 설계된 API를 개발할 수 있습니다. 개발자는 코드와 상호 작용합니다.
"성공의 구덩이에 빠지는 것"의 가장 좋은 예는 jQuery를 사용하여 CSS 선택기 구문을 통해 DOM 요소를 얻는 것입니다. 예를 들어, 클래스 이름이 있는 모든 기사 요소를 가져오려면 jQuery를 사용하여 이를 수행할 수 있습니다.
$("article.blogPost").fadeIn();
선택기 item.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() { ... } });
확장성은 기존 클래스 라이브러리가 모든 것이 완벽하다는 것을 의미하지 않는다는 사실을 인식하게 하고, 자신에게 맞는 클래스 라이브러리를 확장하도록 유도하기 때문에 중요한 측면입니다. 클래스 라이브러리가 확장을 지원하면 새로운 용도를 열 수 있을 뿐만 아니라 수많은 개발자가 일반적인 용도의 이점을 누릴 수 있습니다. 가장 좋은 예 중 하나는 Backbone을 확장하고 "대형 JavaScript 애플리케이션의 구조를 단순화"하는 것이 목표인 클래스 라이브러리인 Backbone.Marionette 프레임워크입니다. Backbone과 같은 라이브러리 확장이 없으면 Marionette와 같은 라이브러리는 매우 복잡해지거나 구현이 불가능할 수도 있습니다.
API 디자인: 단지 라이브러리 코드 작성자만을 위한 것이 아닙니다
JavaScript 라이브러리 작성자가 아니라 JavaScript 애플리케이션 개발자 또는 라이브러리 구현자라면 이 기사의 원칙이 자신에게 적용되지 않는다고 느낄 수도 있습니다. 결국 우리 대부분은 "API"라는 말을 들으면 제가 이 기사에서 사용한 예와 같은 타사 라이브러리를 생각하는 경향이 있습니다.
사실 API는 정의에서 알 수 있듯이 다른 사람이 활용할 수 있도록 격리된 기능을 제공하는 인터페이스에 지나지 않습니다. 이제 중요한 점을 강조하기 위해 오래된 속담을 사용하겠습니다. 실용성을 위해 모듈식 JS 코드를 작성하고 사용 횟수는 중요하지 않습니다.
이 문서에서 참조된 라이브러리와 마찬가지로 JavaScript 코드를 다른 사람에게 노출할 수 있습니다. 코드 사용자가 소규모 그룹이거나 내부 팀이더라도, 심지어 자신만의 비공개 라이브러리를 구축하더라도 이 문서의 API 디자인 원칙과 같은 원칙의 구현에 대해 생각할 필요가 없습니다. 공공 도서관의 저자가 하는 방식입니다. API 디자인 활용의 장점은 단 한 명의 사용자를 위한 것이라도 마치 수백만 명의 사용자를 위한 것처럼 디자인해야 한다는 것입니다.
API 디자인은 개발자의 사용자 경험을 나타내기 때문에 최종 사용자를 위한 UI 디자인만큼 중요합니다. 몇 가지 원칙을 배우고 좋은 예와 나쁜 예를 참조하여 좋은 UI를 개발할 수 있는 것처럼 더 나은 API 디자인도 같은 방식으로 배울 수 있습니다. 이 문서에 언급된 네 가지 원칙과 스스로 발견한 다른 원칙을 적용하면 뛰어난 API를 구축하고 사용자에게 좋은 경험을 제공하는 데 도움이 될 수 있습니다.