2008年8月29日星期五

浅谈developer friendly

Windows XP推出的时候,XP一词几乎到了被滥用的地步。XP大概是指user experience,即用户体验。为软件付钱的是用户,所以用户体验当然重要。而开发者(以下称developer以特指)大概是“用户”的一个特例,当developer在使用一种开发工具、平台或框架的时候,developer的体验也就是用户体验。作为一个developer,很多时候更关注的是开发体验。好的开发体验可称为developer friendly。下面基于自己的体会,结合.NET和ROR两种开发体验的范例,谈谈对developer friendly的理解。

.NET和Ruby On Rails均是最先进的软件开发模式的代表,虽然它们并非一个级别的东西,但思想上有很多共同之处,这些“共通“的地方,大概就是他们”先进“之所在。我觉得它们最大的一点共通之处在于二者都花了很大的功夫在developer friendly上。另一点在于二者都力图在应用开发的不同领域(domain)内提供相同或类似的用户体验,更准确讲叫开发者体验。

先以.NET为例,.NET的出现对developer有两个重要意义:
* 一是对多语言的支持,使得developer能最大限度的利用已有的知识和经验,同时还能从统一的clr中得到同样丰富的类库与run-time支持,vb到vb.NET,java/c++到c#(还有那个被废掉的java版本的.NET实现),javascript到jscript.NET,以及现在的DLR支持下的多种动态语言的实现(ironpython, ironruby, javascript等等),而CLI(和DLR)的标准化,使得在.NET平台上扩展任何编程语言的做法在理论上是可能的。虽然我也总是认为,这只是把同样的东西(.NET framework)用不同的方式(各种基于.NET的语言)来表达,很多时间与精力花在这上面有点浪费,但是从微软的角度看,不论你从哪里来,.NET总是欢迎你,对developer来说,没有什么比直接使用已有的知识在新平台开发更友好的方式了。

* 另一个方面是在不同domain内开发方式上的趋同。原本针对微软平台的产品做开发和二次开发都需要相应的语言和方法,这种”domain"很多,传统的开发方式更多,比如桌面应用(vb, vc++等),web(asp/COM/vbscript/jscript/html/css,iis...),database(T- SQL),office(vba),mobile(embedded vb/vc)等等,一个domain内的熟手要想转到另一domain,是很困难的事情,不仅在于不同domain所需知识的差异,还包括开发方式,编程习惯等很多方面,在跨domain开发时,developer很难应用现有的经验和知识,基本上跨domain的技能都需要从头学起。而.NET从发布至今,微软的几乎所有产品都在悄悄的(或突然的)支持.NET开发,至少在编程习惯和开发方式上是这样的。
- 桌面应用自不必说,从1.0开始,Windows Forms便屏蔽了不同开发工具和语言的差异(几乎只剩下不同语言语法上的差异了),到.NET framework 3.0(3.5/SP1)对WPF的支持,从前使用的SDK和API几乎可以完全忽略。

- web应用方面,对客户端的要求只是浏览器,当大多数编程工作集中在server上时,更可以完全放弃以往ASP+COM粗糙的编程模型,而随心所欲地发挥.NET平台/语言的功效。 asp.NET提供了基于server control的web form编程模型,这种方式几乎屏蔽了web应用本身固有种种复杂因素(stateless, client/server roundtrip, debug, security, validation等等),使得web开发变得如同传统的桌面程序开发那样方便与直观(拖拽,数据绑定,属性设置,双击,编写事件代码,F5运行...),继而在ajax走到前台的时候,ajax也作为server control的extension被加入到toolbox里,在2.0的时候,masterpage,membership等原本需要大量编程才能实现的工作被抽象成了一个个out-of-box的feature,只需配置和简单编程即可使用,等等。当然webform屏蔽了太多细节,为了事件驱动的编程模型增加了太多原本不必要的东西,以黑盒子方式出现的server control给developer带来了很多不安和麻烦。为了顺应developer之意,随后asp.NET mvc作为另一种可选的开发模型被一个preview一个preview地加入进来,借鉴了ROR, django和monorails等成熟的mvc框架后,在强大的.NET framework和visual studio支持下,asp.NET mvc应该会是web开发领域的best practice,虽然其中几乎没有什么微软的创新。随着基于DLR的动态语言的成熟,rails式的DSL也不在话下,JRuby+Glassfish已经做了一个很好的榜样。

- 在database开发方面,无论DDL, DML还是DCL,原本都是SQL(T-SQL)的天下,从SQL Server 2005开始,数据库引擎开始支持.NET assembly,developer再也不必感叹stored procedure和function在实现复杂编程时的限制和不便了,除了部署时有些许麻烦,用vb.NET操作数据库实在是爽(如果有过在stored procedure里实现复杂逻辑的经历会深刻体会到这一点,虽然这不是一个好习惯)。更牛的是针对sql server所提供的报表服务,分析服务和集成服务,SQL Server安装时可以选择安装一个visual studio环境,可以用来在数据库上直接开发和部署这些服务,所以,甚至在生产环境里,developer都可以使用自己最熟悉的开发环境(visual studio)进行编译和部署,微软为developer想的真是周到。

- 基于office产品的二次开发由来已久,没记错的话,office2000以前只能使用VBA开发,2003里office里各产品开始支持.NET开发特性,visual studio里开始安装vsto扩展,到如今,vs2008里默认的项目模板包含了对2003/2007里各种产品的全面支持。office开发和部署原本是一个限制很多而且有些别扭的模式,而现在,把word/excel/infopath作为一种客户端形式,使用熟悉的编程语言,熟悉的事件驱动模型,基于完整的.NET framework,针对office的编程模型几乎与winform和webform完全统一起来,熟练的掌握winform编程的经验几乎可以没有任何障碍的转移到office应用开发上来,加上SharePoint的扩展,这种转移意味着现有的技能可能被放大到一个更宽广的领域。自身技能的可重用意味着经验知识增值和平缓的学习曲线,对任何developer来讲这都是个好消息。

- 还有一点可以说明,微软有一些很重要的努力几乎完全是为了developer friendly而做的。两个domain可以体现这一点,一是基于compact .NET framework的mobile的开发,另一是RIA方面的开发。mobile开发只了解大概,不过有一点比较明确,以前的windows CE上的应用开发一般是使用embedded c++/vb,简言之就是基于Windows CE这个小型的Windows(应该是小型而不是子集)采用非托管代码开发应用,优缺点与在windows上用c++和vb开发应用雷同,mobile开发毕竟不如desktop和web开发更广泛,所以按理说这已经足够了,在资源受限的mobile设备上,应该能简则简。之所以裁剪出个 compact .NET framework,除了移动设备硬件水平大幅度提升,j2me等移动设备上的虚拟机已有先例等原因外,给developer提供便捷一致的开发环境是一个重要原因,曾经做过简单尝试,虽然对mobile开发没有概念,但是按照开发winform的经验建立一个winCE程序(现在叫smart device了吧),拖拽、双击、事件编码,运行,直到一个手机样式的模拟器打开,程序运行起来,几乎没什么障碍,虽然实际的应用开发一定比这复杂得多,但对于一个developer来讲,这种体验必定能让人感觉到友好与自信。

- 更有说服力的是微软在RIA应用与开发上下的功夫,即Silverlight。从Silverlight的产生和发展过程即可见一斑。.NET framework 3.0发布以后,WPF是其中最酷最直观的一个新特性,不过曲高和寡,推广是个大问题,虽然微软想尽办法简化安装.NET framework的过程,也不遗余力的推vista,时至今日,仍然看不到wpf得以普及的希望。于是wpf/e出现了,'e'大概指 everywhere,能称得上everywhere的平台,大概只有浏览器,而基于浏览器的web客户端的局限性不可能从根本上解决,所以只有通过RIA。WPF的“样子 ”(和编程模型)看起来天生就与另一个早已遍地开花的RIA类似,也就是flash(flex)。在RIA上,Adobe与微软殊途同归,一个是从浏览器开始,flash到flex再到AIR,表明Adobe从网络扩展到桌面的野望。微软倒着走,既然暂时看不到wpf得以普及的希望,那就利用wpf给用户带来的印象深刻的使用体验,把wpf推广到浏览器上去。如今,真正基于wpf的应用并不多,对于普通用户来讲也无所谓有无,而所谓印象深刻的“用户”,很大程度上指的是developer,而“使用体验”也更多意味着开发体验。wpf的编程模型很优秀,有时候我甚至会想,桌面编程原本就应该是这种直觉的模型,比如以 tree结构组织的窗体和控件,本来就应该直接映射到xaml这种hierarchy结构上,用来描述UI component外观的属性,本来就该像css那样用resource和template来抽象和重用,等等等等... 从微软的角度看,他们也并没有“发明”这种新的编程模型,只是在向flex和xul等已经存在并经过实践检验的成熟的编程模型趋同而已。不过对于 developer而言,这种趋同绝对是一件好事,一个有经验的flex developer转而开发wpf或Silverlight是一件相对容易的事情,我们可以把wpf和silverlight看成是微软在向所有潜在的 developer示好的重要努力。再回头看Silverlight的发展过程,先前的wpf/e很快更名为现在的Silverlight,从名字到开发方式,都很有规划,很有章法,之前的wpf/e实际上只是一个过度,也许是因为当初有这个想法的时候很着急推出点什么,所以Silverlight 1.x 实际上只是wpf/e的别名,作为过度,它最大的问题并不在于它本身是否具有足够丰富的功能(事实上silverlight从一开始就支持各种媒体播放和很酷的交互方式,下载的run-time也很小),而是对于developer而言,它没有提供一个很好的编程模型,为了host一个silverlight组件(实际上是一个浏览器的plug-in在host),并控制silverlight与html的交互,只能通过javascript来完成。时至今日,vs2008(或其他任何一种IDE)都不能提供绝对完善的javascript编辑环境,而且javascript这种看似平常的动态语言,在充分发挥其动态特性并且代码越来越复杂的时候,会展示出其非常诡异的一面,所以如果silverlight要完全依赖于javascript的话,显然不容易被developer所广泛接受。于是微软推出了一种比较奇怪的版本策略,一方面这个从wpf/e改名而来的silverlight从beta演变成了1.0版,其后的1.x版本也都都基于同样的编程模型(javascript);另一方面Silverlight 2.0的alpha被推出,形式上与1.x同,但几乎整个run-time都被重写。实际上并不是重写,据说silverlight与wpf是从同一个代码树派生下来的,可以说silverlight的run-time是.NET framework的一个子集。第一次看到2.0的这个思路让我觉得非常惊讶,因为这意味着整个.NET framework要被挪到浏览器中,只依赖于浏览器里的run-time,而不论本地OS是否安装(或支持).NET framework。这需要多大的一个run-time啊?!当确认我的想法基本没错后,就马上开始浮想联翩了。因为这意味着.NET developer可以使用最熟悉的语言和环境来开发一个RIA程序,几乎不需要太多的额外知识,意味着一条平滑的学习曲线(当然前提是对wpf、 linq等有足够的了解)。而微软对silverlight运行环境的承诺是只需下载一个2-4M的run-time,难以置信。随后我们看到的是 silverlight的不断改进,包括加入越来越丰富的控件和对动态语言的支持等,同时runtime的尺寸一直控制在可以接受的范围内,直到2.0的 beta版本推出,北京奥运会期间NBC采用silverlight播放网络视频,使其知名度一举飙升。Silverlight 2.0实现到目前的状态,微软花了极大的努力,Scott Gu在一个专访中提到了这些努力,包括如何裁剪和如何尽可能的优化以控制尺寸。回头再看,Silverlight2.0所做的这些努力,几乎只是为了一个目标:developer friendly。(btw:采用同样策略的还有IronPython,1.x版本基于CLR实现,并行的2.x版本基于跟适合动态语言开发的DLR实现)



再看看Ruby On Rails。ROR是一个优秀的web开发框架,虽说与.NET framework并不是一个级别的东西,不过ROR的很多优秀之处体现了和.NET framework同样的理念,重要一点就是对developer而言十分友好。

与.NET开发相比,ROR并不十分强调IDE的先进性,textmate据说是DHH们的最爱,咱没有mac,不过体验过号称为textmate移植版的e-texteditor,老实讲,用惯了visual studio的developer不会觉得他们有何不同凡响之处,不过是一个对ROR开发而言便捷得恰到好处的编辑器。即便更高级的ROR IDE如NetBeans 6.1和3rdrails,也与visual studio有很大差距。而ROR的主要特点是一个框架、一种语言、一套惯例与思路。对developer而言,ROR最大特色是它的全部东西对开发而言都是实用的,也是易用的,换言之,developer friendly。

作为框架,ROR提供了shell command来生成代码,固定的目录/文件结构直接映射到MVC模式,无需配置文件(需要的话也只是几行简单yaml),大量helper方法几乎囊括了web开发中所有常用的功能,扩展很简单,写helper,放在相应的helper文件,写类库,放在lib目录,第三方扩展,放在vender目录,执行任务,运行rake即可,运行和调试很便捷,script中内置方便使用的web server,等等...就像常说的那样“框架为你规定了一切,你只需把精力放在真正重要的事情上”。

对developer而言,过多的选择有时候会让人不知所措(比如在使用php时,单单MVC框架,就有十种以上选择,再加上不胜枚举的模板引擎和数据访问方法,以及他们的组合),在纷纭复杂的选择中,花费了时间,最终却未必得到一个适合自己方案。rails则恰到好处地把所有必要的东西包装(比如ActiveRecord、 ActionPack和ActiveResource)并组织在一起,近乎于best practice,其不同版本对框架结构的调整也体现其与时俱进的生命力。“恰到好处”非常重要,这意味着框架能够在一方面能够满足大部分常规需要(不必开发大量底层重复性的代码),另一方面提供足够灵活的机制让developer有充分的扩展余地。这个“恰到好处”正是针对developer而言,“恰到好处”也是rails从众多类似框架中脱颖而出并被纷纷效仿的关键所在。

作为一种动态语言,ruby几乎是最简洁优雅的(个人认为要强于python)。ruby本身是一种通用语言,但它的动态特性使其经常与另一个词联系在一起,即DSL(Domain Specific Language),这里所说的domain可大可小,可以用来描述某种业务领域,也可以用来代表软件开发的某个方面,比如,html或css都是各自 domain内的DSL,分别用来描述web页面内容/布局和定义style。而Rails本身就是一个典型的用ruby定义的在web开发领域内适用的 DSL。也就是说用ruby语言定义一组在web开发中实用/常用的语法,这套语法接近自然语言,专门适用于web开发。打个比方,"3.years_ago"这样一个语句,实际上是rails为int类型扩展了一个years_ago方法,以简洁易懂的方式代替了通常至少需要几行代码才能实现的功能,即显示3年以前的时间。之所以定义了这样一个方法,是因为在web开发中经常会有计算和显示特定时间的需求,rails里包含了大量此类扩展,但绝不是随意为之,比如不存在一个"3.yeas_ago_plus_two_months"方法,因为就一般意义上的web开发而言,这种方法没有代表性,且不可枚举。而ruby的好处在于其开放性,如果在你的应用中确实会经常用到“n年以前加两个月”这种方法,你可以在helper或lib甚至 rails本身中扩展这样的方法。所以DHH采用ruby来实现rails并非偶然,这样一种灵活、易读的动态语言,对developer而言,是最舒服的选择。这里有个体会:即使使用cakephp或django这些与rails类似的web框架,因为编程语言上的差异(php和python),都没有使用ruby on rails时那种惬意的感受,这只是个人看法。对developer而言,大概这便是friendly的体验。

说到DSL,再补充一点。由前例可以体会,对DSL而言最重要的一点就是对domain恰到好处的抽象。 “恰到好处”可以理解,也就是前面提到的为什么扩展了years_ago方法而没有years_ago_plus_two_months方法。而“抽象” 这个词本身比较抽象,什么样的工作算是抽象?抽象到那种级别合适?没有标准答案。不过在rails里一些重要的方面体现了如何抽象和抽象到什么地步。有三个典型的方面:

* Model: 这个词就是MVC里的M,本身就有点抽象,通常理解为数据模型,在rails里,数据存储介质一般指database(当然也可以是xml,csv或一个 pojo样式的不与任何存储介质关联的实体类),对数据的抽象就是model, ActiveRecord就是model的具体体现。ActiveRecord就是一个ORM,与其他ORM工具的不同之处在于,在rails里,不需要描述实体与db映射的复杂配置文件,多数时候这种映射的依据就是convention,也就是按一些约定俗成的规则做映射,比如一个叫posts的表对应一个叫 post的model类,posts表中一个叫user_id的字段表明users表与posts之间存在一个多对一的外键关联。这种规约看起来没什么大不了,不过对于一个通用web框架而言,“以规约代替配置”这种设计理念是很大胆的,某些时候,这意味着rails里的ActiveRecord可能无法满足一些特殊需求(当然rails提供了足够灵活的机制在需要时可以用代码配置这种规约),而这种代价换来的好处是在很大程度上简化了配置,简化的代码结构,减少了依赖,目的无他,程序对developer而言更清晰简洁而已。ActiveRecord的好处当然不止于此,一旦在实体中定义了 association,就可以便捷的在关联对象之间导航,这种关联可以是1:1, 1:n, n:n, join tables, conditional joins和polymorphic associations,尤其是polymorphic associations,几乎可以实现传说中的OODB了。此外,在ActiveRecord中定义grouping,定义validation,都是非常简洁惬意的事情。在第一次看到LINQ to SQL时觉得这是非常棒的一种数据访问方式,不过随着对rails中ActiveRecord的深入了解,我简直怀疑LINQ是在照抄它的思路了。再加上migration和针对db的rake任务,以及自动生成的test框架,rails中的数据访问相关功能就完整了。

* HTML Template: rails中使用ERB作为默认的模板引擎,ERB本身功能很完善,不过与其他知名的模板引擎相比,似乎并无特别之处,有时反而觉得django的模板引擎更规范一些。rails2.0中也可以使用haml作为模板引擎,如果习惯perl的模板方式,haml也是一种很好的选择,更为清晰简洁。在这里谈起模板引擎,是因为考虑到模板的概念是对传统html的一种抽象。更准确的说,rails提供了一套比较完整的view templates,除了ERB之外,rails还提供一整套helper,比如“form_for",”text_field“等等。我们得理解web应用是由一系列元素构成的,比如客户端的html和javascript,以及服务器端用来控制逻辑和做render的代码,而html中翻来覆去使用的就是 button, div, textbox等等html控件,加之css控制外观,javascript控制行为,才组合成千变万化的web page。如果手工编写一个form,很多工作是重复或大同小异的,rails里的helper们正是对这些重复却又不尽相同的工作的抽象。比如一个 text_field方法,只提供name和value参数,他就是一个textbox,加上其他option作为参数,可以为其定义css,或响应一个 onclick事件,如果再对其扩展,它还可以成为一个带有suggestion功能的textbox。采用常规的方法实现这样的一个控件,我们需要多样技能,比如html,css,javascript,服务器端的ruby代码,甚至ajax(XmlHttpRequest)等。helper的作用就是把这些重复性的工作抽象,包装在一个个望名而知其意的方法里。虽然常用的helper并不多,但rails还是提供了大量的helper,它们可以应用在web开发的方方面面,有些功能甚至是我们想不到的,他们唯一的共同点就是都是在实践中总结出来的,有实用价值的东西,而不是像一个通用类库那样包含了方方面面,但实际上很多东西是用不到的。这种使用简洁的抽象,唯一的受益者就是developer。

* Javascript: 这种东西需要抽象吗?能抽象吗?rails给出了肯定的答案。这里说的是RJS。可以把RJS理解为一种针对ajax特性的DSL。据说高级的 developer是不用这种javascript的抽象的,我的理解是对复杂的ajax操作,RJS有一些局限性,而且RJS以Prototype和Script.aculo.us作为基础库,也妨碍的根据个人喜好使用其他javascript库,比如jQuery。不过这不妨碍我们理解和使用RJS。RJS是一种模板形式,被成为javascript generator template,与通常提交一个动作结果的模板不同,这些模板会生成如何修改一个现有的被提交页面的指令。这可以很容易地修改声明有Ajax 应答的页面内的多个元素。这些模板的动作在Ajax背景下被调用,并可更新页内容。一个简单的例子如”page.insert_html :bottom, 'list',“,它的作用是在当前页面底部插入一个'list'元素。这是一个javascript在客户端操纵html元素的例子,然而作为developer,实现这个功能(以及其他复杂得多的客户端功能),几乎不需要javascript相关知识,因为这里没有javascript代码,只有对javascript的抽象。我想asp.NET的ajax控件扩展,实际上也是类似的道理:即封装javascript以及与服务器交互的功能,屏蔽复杂细节,展现高级特性。 如果不考虑RJS的限制,对developer而言,只要会用ruby,并且了解RJS能完成哪些功能,就足够了。

上述几方面有一个共同的特点:虽然它们针对的是web开发中的不同方面(db访问,html template,javascript),但抽象的结果是不要求developer精通每一方面的知识(比如 SQL,html,css,javascript,ajax等),熟练掌握ruby和rails框架的原理基本上就够了,我想对developer而言,这意味这平滑的学习曲线和快速开发的能力。

最后,说说为什么需要developer friendly。DHH在谈论为什么要使用ruby来开发rails时的一段话很有代表性:

Ruby is a language for writing beautiful code. Beautiful code is code that makes the developer happy, and when a developer is happy, they are more productive. Ruby is unique in the way it lets you express something briefly, succinctly, and beautifully.

没错,软件开发的主体是developer,用户会选择产品,但不会去选择开发工具与框架,只有developer(广义上的)才会在意这些,developer的取向直接引导了用户的使用习惯,Windows 的成功说明了这一点。让developer感到舒服,不仅会提高生产力和产品质量,也意味着你的框架,你的平台将被广泛接受,直接影响的就是你的市场占有率。

我想,当鲍尔默在微软开发者大会上歇斯底里的高呼”developer!developer!!developer!!!“的时候,大概想到的也是这些。

没有评论: