`
hax
  • 浏览: 962596 次
  • 性别: 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 的抉择,好让人纠结

相关推荐

    Web常用UI库 kissy.zip

    Kissy 是一个由淘宝开发并开源的JavaScript库,它的设计目标是为Web开发者提供一套全面、强大且易用的UI组件。Kissy 的出现旨在简化Web应用的开发过程,提高开发效率,尤其在构建复杂的前端界面时表现出强大的优势。...

    kissy学习教程

    而KISSY框架则针对这些问题提供了一个全新的解决方案,它致力于模块化、跨终端支持以及易用性,特别适合于开发电子商务网站等复杂交互的Web应用。 KISSY框架的适用场景主要包括Web页面开发,尤其是电商平台的页面,...

    提示补全组件:Kissy Suggest

    标题中的“Kissy Suggest”是指Kissy框架中的一个组件,Kissy是一个轻量级的JavaScript库,专注于前端开发,提供了丰富的组件化功能。Suggest组件则是Kissy中用于实现自动提示、补全功能的部分,常见于搜索框或...

    kissy 学习

    Kissy 是一个由淘宝开发的前端JavaScript库,它旨在简化Web开发,提高代码质量和性能。Kissy 提供了一种模块化的解决方案,使得开发者能够更好地组织和管理他们的代码,同时提供了丰富的组件来支持日常的Web开发任务...

    Kissy Suggest 自动提示例子

    Kissy Suggest 是一个基于 JavaScript 的开源库,专为实现自动提示功能而设计。本文将深入探讨 Kissy Suggest 的工作原理、使用方法以及其在实际项目中的应用。 Kissy Suggest 是 Kissy 项目的一个组件,Kissy 是一...

    修改的Kissy富文本编辑器

    Kissy是一个轻量级的JavaScript库,专注于前端开发,而Kissy富文本编辑器则将这种轻量级的优势带入了内容编辑领域。 Kissy Editor的核心特点包括但不限于以下几点: 1. **模块化设计**:Kissy Editor遵循Kissy框架...

    KISSY 小巧灵活、简洁实用的 UI 类库.zip

    KISSY,一个在前端开发领域中被广泛使用的轻量级UI类库,以其小巧、灵活和实用的特点,赢得了众多开发者们的青睐。本篇文章将深入探讨KISSY的核心特性、应用场景以及如何有效地利用它来提升开发效率。 首先,我们来...

    js编辑器KISSY和TinyEditor

    在实际使用中,KISSY可以作为项目的基石,提供底层的模块管理和UI组件,而TinyEditor可以作为其中的一个模块,用于实现页面内的文本编辑功能。两者结合,既能享受到KISSY带来的强大功能和可扩展性,又能利用...

    基于KISSY的动画导航.zip

    KISSY是一个轻量级、模块化的JavaScript库,尤其适合中国开发者,因为它内置了对中文字符集的支持,并且提供了丰富的组件和API,便于构建复杂的Web应用。下面我们将深入探讨这个项目可能涉及的关键知识点。 1. **...

    使用KISSY实现视差滚动效果

    在本文中,我们将深入探讨如何使用KISSY框架来实现视差滚动效果。KISSY是一个轻量级的JavaScript库,旨在提供一个简洁且强大的工具集,帮助开发者快速构建复杂Web应用。视差滚动是一种增强网页用户体验的视觉技巧,...

    新增KISSY图片裁剪插件.zip

    KISSY 是一个轻量级的JavaScript库,旨在简化前端开发工作,它提供了丰富的组件和API,可以方便地进行各种前端任务,包括图片裁剪。"新增KISSY图片裁剪插件.zip"这个压缩包文件就是一个利用KISSY实现的图片裁剪功能...

    imagezoom:KISSY 图像放大

    "imagezoom: KISSY 图像放大"是一个基于JavaScript库KISSY的插件,用于实现网页中的图像放大功能。KISSY是一个轻量级、模块化的前端开发框架,它提供了一种简单的方式来组织和管理JavaScript代码,尤其适用于大型Web...

    digitalScroll:基于 KISSY 和 Layer-anim 的 javascript 数字滚动小部件

    Layer-anim则是KISSY的一个扩展,专门用于实现动画效果。它提供了丰富的动画API,开发者可以通过简单的调用来创建出各种复杂的动画,包括平移、旋转、缩放等。Layer-anim与KISSY的结合,使得digitalScroll能够实现...

    top:返回顶部的kissy组件

    Kissy是一个轻量级、模块化的JavaScript库,它允许开发者通过简单的API来构建复杂的Web应用。 在JavaScript编程中,这样的组件通常实现为一个小型的插件或者函数,它可以被绑定到页面的某个元素上,如一个按钮或...

    bui后台示例

    【BUI后台示例】是基于BUI和Kissy框架构建的一个管理页面示例,它主要展示了如何在后台系统中实现高效、易用的交互功能,包括文件上传和表单表格的处理。在这个项目中,BUI(Business UI)作为前端用户界面库,提供...

    淘宝在线编辑器JAVA版_带文件上传

    淘宝在线编辑器JAVA版是一款基于KISSY Editor的富文本编辑器,专为Java开发者设计,支持文件上传功能。KISSY Editor是阿里巴巴开源的一款强大的JavaScript富文本编辑器,而这个JAVA版则提供了与服务器端交互的能力,...

    draggable.js源码分析

    首先,draggable.js是基于KISSY框架实现的,KISSY是一个开源的JavaScript组件库,提供了丰富的DOM和事件操作API,为构建复杂Web应用提供了便利。在draggable.js中,主要利用了浏览器的事件模型和DOM操作,实现了拖拽...

    淘宝的HTML5实践.pdf

    - **背景**:考虑到淘宝用户中仍有一定比例使用老旧浏览器的情况,淘宝团队开发了KISSY库,旨在提供一个兼容性强且功能丰富的前端框架。 - **解决方案**:KISSY库针对不同的浏览器环境提供了不同的实现方案。例如,...

Global site tag (gtag.js) - Google Analytics