`
hax
  • 浏览: 951158 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

论KISSY中一则API的设计

    博客分类:
  • UX
阅读更多


这次D2上,玉伯介绍KISSY。我听下来,KISSY有不少设计和我断断续续做的PIES框架的设计是类似的(或许现在的JS类库在许多设计思路上必然会有许多共识)。但是seed、meta之类的概念就不容易理解。后来我看了一下,原来这些概念是爱民给加上的,嘿嘿。当然,即使不看爱民的解说,我们也可以了解大概思路,是系统可从seed动态演化出来。但是meta这样的抽象就有些学院派,特别是不像爱民的QoBean项目本来就是要做元编程,在KISSY内,这个抽象只对内,不对外,也不直接解决实践中构建系统的关键问题,比如namespace和module。meta的作用其实只是使得内核具有一个更加精巧的核心。所以这样过于“务虚”的抽象对于听众来说无疑是天书。

好吧,这个起头有点长,其实本篇blog想要讨论的只是KISSY中的一个很小的点:DOM.attr的API设计。

这个API(我稍作了简化)是这样滴:

attr ( element, attrName )
获取元素的属性值。

attr ( element, attrName, value )
给元素设置属性值。

你可以注意到,这里其实是函数重载(overload),两个参数是返回属性值,而三个参数是设置属性值。KISSY中有不少API都是类似的。

然而这种API设计我认为大有问题。原因很简单,重载方法(函数)的参数虽然不同,但是同样名字的方法(函数)干的事情应该是相同的。而在这里,两个重载方法(函数)做的事情却正好相反!

我们考虑一下实际编程的情况,许多时候参数本身就是一些方法调用的结果,因此整个调用序列可能就比较长。在这种情况下,你很难一眼看出这个调用到底是几个参数,因此当然也不知道这里到底是取值还是赋值。

所以这种API可能导致程序可读性的严重下降。

那么在什么情况下,相同名字而相反意义才是可行的?属性就是这样一个例子。因为a和a=(赋值)的区别还是非常明显的。然而即使如此,我们还经常遇到a=1和a==1的混淆。

另一个可行的案例有些语言是用a()和a(v)来作为getter/setter的写法。因为“没有参数”的调用和带有一个参数的调用,其区分还是相当明显的,况且这些语言中很可能允许省略无参调用的括号,那么a和a(v)的差异就更一目了然了(而且还不会有a=1和a==1的混淆)。但是,有一个参数和有两个参数的区分就不那么明显了,而像attr这个例子是两个参数和三个参数,那就更难分辨了。

所以回到最普遍的情形,对于方法(函数)的重载来说,应该坚守这个原则:重载方法(函数)干的事情应该是相同的。相比attr,setAttr和getAttr虽然多了几个字符,但是能确保程序的可读性。






6
8
分享到:
评论
22 楼 hax 2010-12-28  
@玉伯
既有Node(类似jQuery的$)又有DOM,确实比较奇怪啊。我原来以为KISSY没有$的。

可是说到整体一致性,$方式和静态方法的方式区别甚大,即使你的API都叫做attr,但是前两者的区别仍然远胜于attr一个名字的一致。

我觉得,愿意用DOM.attr的人却不接受$(xxx).attr的话,实在是很奇怪。
21 楼 lifesinger 2010-12-27  
同意 hax 的分析。但还缺一个考虑:api 的整体一致性。

我最开始的回复,提到了一个演化,最开始 DOM 上是 getAttr/setAttr, Node 上是 attr. Node 对应 jQuery, 绝大部分 api 都是模仿 jQuery.

习惯了 jQuery 的前端,喜欢用 S.Node(selector).css(...).html(...)
不喜欢 jQuery, 熟悉 YUI2 的前端,比较排斥 Node, 喜欢用 DOM/Event 方式组织代码

这就导致问题,一个项目里,既有 setAttr/getAttr, 又有 attr. 给 code review 带来了麻烦,维护他人代码时,也得在两种思维见跳跃。

因此权衡考虑,DOM 上,也统一成了 attr. 这带来了整体一致性,提高了代码的整体可读性。

如果没有 Node 的话,我同意 DOM 的实现里,用 getAttr/setAttr 更妥。但有 Node 的情况下,DOM.attr 是牺牲个体,成就整体,还好的。
20 楼 hax 2010-12-27  
@玉伯

第一个例子我文中已经解释过了,为什么一般的getter/setter没有问题,而attr(...,...,...)是有问题的。

有个事情要补充,确实KISSY是模仿了jQuery的API。其实jQuery的API确实也有我说的问题,所不同的是,KISSY的attr比jQuery还多了一个参数,实际就是把实例方法变成静态方法了。恰恰是这一点把缺陷放大很多。

归根到底是JS语言之前没有getter/setter以及到现在也没有missing method的处理方式,所以无法能做出很易读的语法。包括eventListener += / -= 的例子也是类似的。

需要注意的是,jQuery虽然也有我提到的问题,但是相对较轻。第一个是它是实例方法,因此少一个参数。其次,第一个参数实际使用中基本上都是直接写字符串常量。

简言之,jQuery的API仍然在很大程度上保持了getter/setter的形制。而当变成静态方法之后,虽然看似仅仅是很小的变形,但是却基本上失去了getter/setter的形制,特别是上述两点正好都丧失了(即多了一个参数,且第一个参数常可能是复杂的表达式的结果)。

还有,我谈的虽然看似是相当主观的问题,但是其实我的结论并不来纯粹自于感觉,而是建立在分析和推理上。
19 楼 lifesinger 2010-12-26  
看这个例子:

var html = elem.innerHTML; // getter
elem.innerHTML = 'val'; // setter

同是 innerHTML, 根据使用场景来决定是 getter 还是 setter. jQuery 的 API, 我觉得模仿的是上面的方式。

至于 addEventListener/removeEventListener, 让我想起:

document.cookie = val1;
document.cookie = val2;

上面的写法是 add 的概念。如果能:

elem.eventListeners = fn1;
elem.eventListeners += fn2; // add
elem.eventListeners -= fn1; // remove

也是不错的,只是考虑老旧浏览器的限制,和参数传递,上面的方式实际操作起来并不好。

jQuery 不用 addXxxYyy/removeXxxYyy, 和 write less, do more 的理念分不开,html/css/attr 等方法,个人觉得是对 innerHTML 这种 API 的模拟。对于 addEventListener/removeEventListener, 对应的是 on/un.

hax 的核心理念是 attr 这种方式有损可读性,但是 api 风格这件事,不能只看缺点,还得看优点,attr 的优点是提高了易用性。可读性和易用性都是“主观感觉”,和团队习惯相关。因此:

1. 如果团队觉得 attr 带来的易用性 < 可读性,就选择 getAttr/setAttr 就好;
2. 反之,用 attr 蛮好的。

两全其美,自古难得。
18 楼 虚怀若谷 2010-12-26  
这种写法,有人说易读,有人会说难读,这是很正常的事情,个人觉得不用太去纠结了。你爱怎么写就怎么写。

但有一点必须得遵守:你在overload的时候,它本身拥有的功能是绝对不能丢的。

element.setAttribute 在ie下面是有第3个参数的,被你们一整,不支持了。同样element.getAttribute 在ie下面也是有第2个参数的,也不支持了。

大家不要小看setAttribute的第3个参数以及getAttribute的第2个参数,这个参数是非常有用的,等你们真正对上眼了,我想你肯定会跪倒在她的石榴裙下面的,哈哈。

YUI也有自己的getAttribute和setAttribute,整进框架后,照样不支持,遗憾啊遗憾啊,让我这种非常遵守团队框架使用规范的人情何以堪啊!
17 楼 Army 2010-12-26  
其实这里举个最好的例证就是:

所有dom原生API都是和此文表述一致。比如addEventLister、removeEventLister;没见过eventLister()根据参数个数确定是add还是remove。

相反,jq当中语法风格都是这样。

问题就变成了,为什么会有这两种相反情况的出现?
16 楼 hax 2010-12-25  
另外,可注意到我之前举的反例,如 a 与 a= ,a 与 a(v) 或 a() 与 a(v) ,其实也会有类似的问题,但是程度非常轻。比如 a\s*=[^=] , a\s*\( , a\s*\(.+\)就基本可以确定是 set 了(除了极少数语法允许但实践中不会出现的特例)。

要之,如果机器都不能很快的辨识,人就更不可能了。
15 楼 hax 2010-12-25  
@玉伯

DOM.css正好是一个例子,当我在浏览器里打开你这个页面源码后,我首先发现大多数调用貌似都是set。然后我想统计一下,在总共64次调用中,到底是多少次set?然后我突然发现,没法做这个统计!!

显然,简单的find字符串功能是不可能区分的。

然后我需要怎样的工具?显然依赖参数个数是不可能的,因为那需要JS parser才能确保正确。那么用这个正则如何:\n\s*DOM.css,认为之前可以找到换行的就是set调用。这貌似可以解决问题。但是转念一想,这其实有赖于你的编码规范,因为你可以写这样的:

var a = ....long code.... && parseInt(
  DOM.css(......,......)
)

你可以这样写:

xxx.forEach(function (x) { DOM.css(...,...,...) })

还有这个:

【将来JS可能允许省略return】
var array2 = array1.map(function (x) { DOM.css(......,......) })

【其实现在也有二义的情况】
return condition ?
   DOM.css(......,......) : DOM.css(......,......);


其次,在代码中其实有许多DOM.css连续调用,它也许可以是链式的。
DOM.css(.......,.......,......).css(.......,.......,......).
    css(.......,.......,......).css(.......,.......,......);


上述所有这些,单独看或许都不是什么大问题。但是结合起来看,你就知道问题了,那就是这样的API设计如果要保持易读(对机器、工具来说就是方便处理),要对代码风格做出约束。而我认为API不应该做这种假设。
14 楼 lifesinger 2010-12-25  
@hax:

搜索了下淘宝的代码,DOM.attr 使用场景不多,我们以 DOM.css 为例,看一下淘宝宝贝详情页面的js代码:

view-source:http://a.tbcdn.cn/mods/??slide/slide.js,c2c-head/c2c-head.js,sku/sku.js,rank/rank.js,switch/switch.js,top/top.js,guide400/guide400.js,b2c-head/b2c-head.js,b-usetips/b-usetips.js,deal-record/deal-record.js,recommendation/recommendation.js,tabbar/tabbar.js,relative/relative.js,reviews/reviews.js,guestbook/guestbook.js,other-info/other-info.js

总共有 64 处调用了 DOM.css, 人肉扫描了一下,任何一处是 getter 还是 setter, 都是蛮清晰的,规律很简单:

独立的 DOM.css(...) 语句是 setter
其它位置上的 DOM.css(...) 是 getter

我觉得可读性更多取决于代码的组织习惯。命名很重要,但 attr 还是 setAttr/getAttr, 对可读性的影响并没有想象中的那么大。attr 可能会让尚未熟悉该方法的读者带来迷惑,但这个迷惑是可以通过学习,稍加熟悉就可以克服的。然而 attr 带来的好处是便利性,更少的输入,更“清爽”(和团队习惯和社区氛围有关)的代码,提高了可书写性。
13 楼 lucane 2010-12-25  
小弟觉得在JS中对属性塞值和取值用attr()好,简单,也不会有太大的歧义吧,除非个人喜好觉得不能这样做

还有我觉得fins说的这个在底层方法做到id和$id都能适应就可以了,在上层方法中个人觉得还是同意fins的,团队之间了解学习了,就知道这个方法怎么调用,写这么多代码来适应所有情况也没有必要
12 楼 hax 2010-12-25  
@玉伯

我讲“必须”,自然是指最佳实践,因为语言本身没有办法保障你重载的方法是干“一样的事情”。而所谓“一样的”,其实是指方法调用的用意是一样的,或者可以理解为对象在收到消息(方法调用)后,其行为或状态变化是类似的。setter/getter显然不在此列。以一个最浅显的判断标准,setter会改变对象状态的,而getter通常不应有side effect。

而可读性呢,你说的那个是比较理想的状况。而实际上方法调用可能是链式的或者在一个复杂的嵌套调用中,再加上本身参数也可能是一个很长的表达式,还有你可能有程序代码折行导致DOM.attr(...)虽然在行首,但是实际上不是set而是get的情况。你可以比照 a = v 和 a == v的情况。虽然说set/get只差一个字母,但是就是这一个字母,就很大的提高了可读性。
11 楼 fins 2010-12-25  
回9楼:

其实咱俩说的是不同的问题 嘿嘿
10 楼 lifesinger 2010-12-24  
> overload所做的事情必须是一样的

这个是最佳实践,不是“必须”的。

“一样的”也存在模糊性,attr(el, n) 和 attr(el, n, v) 都是对属性进行操作,从这个层面讲,也可以说是“一样的”。

hax 担心的是这会降低可阅读性,实际上,是 setter 还是 getter,并不用去数参数个数,而是根据环境去判断的:

var x = DOM.attr(...);  不用看参数,也知道是 getter
DOM.attr(...); 也一样,一眼扫过去,立刻就知道是 setter

从语义分析上看,用 attr 表示多个含义,我觉得并不会降低可读性。可能还是一个习惯问题。
9 楼 hax 2010-12-24  
fins同学完全没有抓到要点啊!我其实挺喜欢overload提供的便利的。

我这里讲的一点主要是,overload所做的事情必须是一样的,不能做不一样的事情,因为那样你就需要从参数去区分到底是在干什么。

另外一个overload需要注意的问题是,注意类型。比如x(element, string),注意不要重载一个x(string, element),即仅仅位置变化。因为JS里更习惯重载一个x(selector, string)。
8 楼 Army 2010-12-24  
其实还是角度的问题。强类型或静态语言出身的人,大多都不适应;php、js、设计等出身的人,反而很喜欢这种风格。
真是够讽刺的。
7 楼 fins 2010-12-24  
Army 写道
再如包装 dom的函数
function wrappDom(dom) {}

这个我持相反意见,这里正是体现了overload的地方:
wrappDom(dom)
wrappDom(id)

我觉得到这个地步刚刚好。


嗯 这里面确实有个度的问题.而且我这个例子举的不恰当.
下面这种多态应该完全是没必要的吧 呵呵 (这个例子有点夸张)

method( A, B, C );
method( A, C );
method( A, B );
method( B, C );
method( A );
method( B );
method( C );
6 楼 Army 2010-12-24  
再如包装 dom的函数
function wrappDom(dom) {}

这个我持相反意见,这里正是体现了overload的地方:
wrappDom(dom)
wrappDom(id)

我觉得到这个地步刚刚好。
5 楼 fins 2010-12-24  
这点上 我站在Hax一边 呵呵.

我从另一个角度简单说一说我的看法.


在大型的框架 或者是将框架应用到大型的项目中
这里的大型不仅仅是 项目本身, 还包括开发团队.

我一直从事企业级应用开发,经常面对的问题就是:
项目由100多个人来开发,大家按模块划分(分层开发在大团队里只是一个梦), 也就是说100多人 每个人都要从 前台写到后台.

而这些人中, 很多对js的了解有限. 尤其是很多人几乎很少接触动态语言.
因此js的动态性 常常给大家带来困扰.
于是 我们常常在指定开发规范时, 有意限制一些js 的灵活性.

典型的例子就是, 很少使用所谓的多态, 以及动态参数(两者其实是一回事).
每个函数的参数个数 参数类型 都是固定的.

例如设置宽度的函数:

function setWidth( width ) {}

很多框架为了灵活和简单易用, 可以传如 12, '12', '12px' .

再如包装 dom的函数

function wrappDom(dom) {}

很多框架也很灵活, 参数可以是一个dom对象, 也可以是字符串,如果是字符串 那么就当作ID或selector来处理, 然后在函数内部 先取一下dom.

诸如此类的.

而我们在设计企业级框架的时候, 常常会避免这些做法(当然 还是要有个度, 完全丧失灵活性有时候会给使用带来很大的麻烦)


wrappDom函数只支持 dom对象做参数. 如果你有一个id, 那么 对不起,请你自己先用 $id取一下dom吧.
于是代码可能变成了 :
wrappDom( $id(x) )

是的 代码变多了, 使用麻烦了, 但是 这种死板的 机械的 用法, 很多时候 是最好的.











4 楼 luolonghao 2010-12-24  
jQuery这么设计目的应该是减少函数(方法)名长度,这和jQuery的理念(write less, do more)一致,其实这个理念和可读性是相互矛盾的,不过jQuery正好找到了平衡点, 保证可读性的前提下尽量缩减函数(方法)名长度,看过一次文档后函数(方法)名不会容易忘记。有些类库的A(),H(),R()这样的名字就明显过头了。
3 楼 lifesinger 2010-12-24  
没写完,不小心 Ctrl + Enter 了,继续:

近期又再将 attr 等方法分拆,成为:

Meta.DOM.getAttribute
Meta.DOM.setAttribute

然后变换到

DOM.attr

再进一步变换到

Node.attr

api 的抉择,好让人纠结

相关推荐

    kissy中文文档

    高发展前途的web前端开发利器--kissy,中文文档。拥有该文档后不必再上网到处找,一切尽在其中。

    KISSY 1.4.8

    KISSY 1.4.8,一个淘宝用的类似jQuery的前端框架

    Kissy学习教程

    Kissy学习教程.rarKissy学习教程.rarKissy学习教程.rarKissy学习教程.rar

    kissy源文件代码

    KISSY Editor 是开源项目 KISSY UI Library 的一个组件。KISSY 目前基于 YUI 2.x 开发,目标是打造一系列小巧灵活、简洁实用、使用起来让人感觉愉悦的 UI 组件,目前已有 CSS 基础框架、搜索提示 Suggest 和 KISSY ...

    淘宝KISSY前端开发框架

    KISSY提供稳定的核心,包括 oo、dom、Event、Anim、Ajax 等;强大且易用的脚本加载器,特有的 loader;丰富的UI组件,包括 Switchable、Overlay、Draggable and Droppable 等。KISSY具备模块化、高扩展性、组件齐全...

    kissy-1.4.8.zip

    kissy-1.4.8.zip,淘宝最新前端开发包

    js KISSY框架阿里云滑动下拉导航菜单效果代码

    js KISSY框架阿里云滑动下拉导航菜单效果代码 js KISSY框架阿里云滑动下拉导航菜单效果代码 js KISSY框架阿里云滑动下拉导航菜单效果代码 js KISSY框架阿里云滑动下拉导航菜单效果代码

    kissy 框架

    淘宝发布开源编辑器:KISSY Editor,和我们在 WordPress 后台使用的富文本编辑器 TinyMCE 一样,它可以让我们在线编辑和格式化文本,但是相比 TinyMCE KISSY Editor 更加轻巧,更加适合国内的网络环境。

    kissy-1.4.8.rar

    高发展前途的web前端开发利器--kissy,中文文档。拥有该文档后不必再上网到处找,一切尽在其中。

    Web常用UI库 kissy.zip

    Web常用UI库 kissy ,kissy 是淘宝一个开源的 JavaScript 库,包含的组件有:日历、图片放大镜、卡片切换、...

    kissy_editor

    KISSY Editor 是开源项目 KISSY UI Library 的一个组件。KISSY 目前基于 YUI 2.x 开发,目标是打造一系列小巧灵活、简洁实用、使用起来让人感觉愉悦的 UI 组件。目前已有 CSS 基础框架、搜索提示 Suggest 和今天发布...

    Kissy 15天学会.zip

    Kissy 15天学会.zip欢迎下载!!!

    kissy editor 2.0

    淘宝帮派编辑器 kissy editor 2.0

    kissy 学习

    淘宝kissy帮助文档 学习文档 用法等

    kissy-mobile:KISSY Mobile 是阿里巴巴内部项目Kissy MINI fork出来的项目。是面向移动终端的KISSYApp.svelte版,在保持API和KISSY一致的情况下,着重优化、精简核心模块代码,保证高可用的同时做到身材苗条

    是面向移动终端的KISSYApp.svelte版,在保持API和KISSY一致的情况下,着重优化、精简核心模块代码,保证高可用的同时做到身材苗条。Kissy MINI请参考 本人觉得这是一个非常好的移动端框架,有着体积小,速度快,模块...

    KISSY Editor 1.0.0

    淘宝用过一个很古老的编辑器(至今宝贝发布页面还在用)。去年年底,换用过 YUI 编辑器(让人爱恨交加)。今年,祭出了 FCKEditor. 从纯功能上讲,FCK 是王者。但在国内互联网环境下,FCK 的文件有点大。针对淘宝的...

    异步上传组件uploader基于kissy

    异步上传组件uploader基于kissy,拥有flash,iframe,html5三种方式

    kissy学习教程

    KISSY框架是目前硕果仅存的几个国产js框架之一,在阿里的网站比如淘宝网、天猫等大量使用。

    KISSY Editor 一个小巧灵活的网页编辑器

    KISSY Editor 一个小巧灵活的网页编辑器

    kissy模块化实践

    kissy模块化实践.taobao 前端架构Kissy介绍

Global site tag (gtag.js) - Google Analytics