界面开发中的DSL与定制控件

“中等程度的定制化”太含糊,按俺的观点,基于DSL的方案就适合“不需要自己写控件”的场合,理由很简单:a) 控件质量有基本的保证,b) 不会有过于怪异的用法,比如Windows这边最喜欢干的多次重入同一个消息循环之类的。反过来像游戏UI那种基本上要求100%定制的还是免谈了。⋯⋯

——陈甫鸼对《界面开发中的DSL》的评论

定制控件(俗称自己写控件)被很多人——好吧,被我——看成界面开发领域专业水准和业余水平的分水岭。快速应用开发(RAD)的鼓吹者通常回避讨论定制控件。企业开发和原型产品绝少需要定制控件,而大多数用户天天使用的杀手级(killer)通用软件又绝少能避免引入定制控件。

在开发中对定制控件的需求一定程度体现了UI framework本身的功能和一致性的强弱。出于功能实现的目的必须引入一个定制控件意味着framework的内键控件不能满足基本需求。由于一致性原因引入定制控件一般是因为缺省控件的用户体验达不到需要的水准,这种情况下缺省控件的水准——比如控件的观感(look and feel)——一般在原型产品中可以容忍,但是会阻碍软件达到高可用性。由于功能原因引入定制控件不意味着framework本身的功能一定有缺陷,特定的应用需求不一定适合通过framework提供内键控件来满足。经常由于一致性原因引入定制控件往往意味着framework本身的品质不够精致。比如在Windows下无数的应用使用自己定制的滚动条(scroll-bar),而这种情况在Mac OS X里非常罕见。

UI DSL对定制控件并不友好。给定一个DSL,它与不同的定制控件配合的默契程度取决于该控件的定制程度。比如,如果一个定制控件仅仅修改内键控件观感(基本上就是override on-draw的处理)而保留内键控件其它一切行为,那么它还是可以通过DSL来制定位置、大小等大多数属性。在处理DSL的可视化工具里也能正确显示控件的位置、大小(观感还是缺省的,开发者设计的时候自己想像最终的效果吧)。所以如果系统的内键控件如何具有良好的一致性,就能避免很多的控件定制,从而让DSL能更好的发挥作用。而一个从外观到行为都自己实现,不以任何内建控件(除了基本的panel、group或者canvas)为基础的定制控件在DSL及其处理工具中能被处理到什么程度就很不好说了。

更深一层来说,DSL不能很好的处理定制控件是因为声明式(declarative)语言和控件的命令式行为的差异决定的。由于声明式语言的局限,DSL处理控件时通常自然而然的局限在只处理控件的初始状态。而处理DSL的可视化工具在决定控件的初始状态的时候往往依靠的是本身的知识。比如,一个可视化工具在设计时显示一个对话框和它上面的按钮的时候,这个工具通常并不能通过询问UI framework(比如Win32或者Cocoa)来得知这个对话框和这个按钮要如何显示(这样做通常意味着要运行正处在开发阶段的程序,这是昂贵的而且经常是违背因果的),相反,工具本身需要拥有如何显示按钮和对话框基本状态的知识。

这种方式有一些严重的问题。首先是同样的知识要在控件本身和DSL的定义以及处理DSL的工具中重复体现。保持多个部分体现的一致的知识并非是简单的事情。我们经常可以看到,在新版本的操作系统中开发时旧版本的可视化工具显示的控件和程序真正运行的时候显示的风格相差甚远。其次,DSL无法预测晚于其语言定义和处理工具出现的控件有什么样的运行或者初始状态,这让语言本身及其处理工具只能处理有限集合以内的内键控件。

解决这些问题的努力所知不多。Sun的JavaBean标准曾经做出过努力。JavaBean的方案是在控件的发布包中(JavaBean是一组字节码文件,打包成.jar)加入一组设计时信息(design-time information)。设计时信息包括控件的初始外观等信息,也由声明型语言定义。JavaBean标准对于一个JavaBean是否提供设计时信息是不强制的。在实践中JavaBean的方案并不成功。因为组件的粒度问题,软件组件从来没有像建筑零件那样做到预制化,所以设想中的独立组件市场(resuable component market)从来没有成为现实。没有市场的需求,大多数JavaBean都是开发者为自己的应用内部开发的定制控件,也就没有投资于提供设计时信息的动力。绝大多数内部使用甚至很多出售的JavaBean都不包含设计时信息。JavaBean的做法也没有真正解决重复实现的问题,虽然控件的运行时外在特征和设计时特征由统一的发布包提供,但是在包的内部仍然是由分离的代码来实现的。开发者仍然需要额外的保持多处代码的功能同步。

现在的DSL对定制控件的应用大多数是用占位件(placeholder control)来实现。DSL在定义中加入某些内建组件或者纯粹的占位件(例如,在可视化工具中表现为简单的矩形)。代码在运行时讲控件替换为定制控件的行为。由此,DSL可以定义定制组件非常有限的一些行为。

DSL本身在界面设计中的应用并不完美,对定制控件的处理尤其差。但是一些试图解决这些问题的努力最终因为缺乏动力而失败,从另一方面说明DSL本身的缺陷也许并非界面设计中开发者非常急迫解决的问题。这是我们在软件开发中经常遇到的一种情况:某种问题被提出,人们付出了相当的努力试图解决,但是提出的解决方案最后被废弃,虽然有事是因为解决方案自身的缺陷缺陷,但是更多的是因为使用者缺乏被采用这些方案的动力。这说明问题的提出过于理论化,问题的提出者高估了该问题带来的不便或者低估了人们对该问题的容忍程度,而解决方案本身的不完美又无法被其带来的益处所抵消。(说句题外话,文件系统领域,对于粒度比UNIX的user-group策略更细的permission control的需求就是这样一种问题,当理论者提出并试图用访问列表ACL来解决这个问题的时候,却发现大多数系统管理员都认为user-group策略是足够的而缺乏配置ACL的动力。)

一条回应 to “界面开发中的DSL与定制控件”

  1. sipoint Says:

    WordPress的导入有点问题。甫鸼同学在Wordpress-china.com上留的一段评论没法导入:
    ==========================================
    好吧,俺得梭让俺桑心滴素俺滴名字被写错鸟,如此不如直接写“福州”鸟,唉。

    — 火星文完毕,接下来的文字来自冥王星 —

    注意:以下文字对Windows信徒的打击可能比较大,而且俺无意论证GTK+好于Windows,请同学们不要对号入座,谢谢。

    关于DSL俺用得很少,也没什么好补充的,但有一条俺持保留意见:俺认为是不是需要自定义控件与framework品质没有多少关系。它主要与该framework最初的设计目的和后来的质量控制有关。

    还是GTK和Windows默认的控件作比较,有一个例子很能说明问题:GTK+很早就有GtkHSV这种控件。熟悉计算机绘图的同志们都认得HSV是用来表示一种color wheel的,也就是计算机中的色彩选择环图的一种。反过来,Windows API也发展了这么多年,就偏偏没有内建一个类似的控件。那么这是不是说GTK+比Windows精致呢?不是,GTK+需要这个控件的原因是因为它源自Gimp,换句话说他们就是专门搞2D图像处理出身的,而Windows的基础控件基本上都是为了Windows内建的那几个程序准备的(Explorer,Notepad,Mspaint)。所以Windows team认为没什么必要专门做一个用不到的控件,这也是为了减少维护的成本。至于Mac,他们本来在多媒体上遥遥领先,而且和Adobe这样专业做图形的耳鬓厮磨了这么多年,UI上的工作多得很,一来二去早就练出来了。

    至于Windows的控件为什么会发展成现在这个样子,俺的观点是它与Windows项目的开发方式有关。早先Windows系统默认集成的UI部分不是独立的子项目。所有最初内建的控件(主要定义于user32.dll)主要是为了Windows最初的内建工具而开发的,比如Explorer和notepad。而历史上很多其它项目是先以独立项目的形式出现,然后再集成进Windows的,比如所有的Windows Server管理员都很熟悉的MMC和IIS,还有地球人都知道的IE。这类项目立项之初必须面对的问题在于Windows内部的控件满足不了他们的需要,比如MMC支持拼装的列表和支持图标动态变化的菜单条,还有从IE6开始的bookmarks菜单。而这时Windows team出于项目维护的考虑又不会对他们予以支持。所以他们都选择了自定义控件来满足需要。同样的事情也发生在Visual Studio和Office这样的大team身上。而且最后他们也没有将这些控件统一包装成一个库。换句话说,现在Windows数目庞大的控件是从各自为政的不同项目里一点一点凑起来的,而且不相统属。

    Windows这种组织带来的麻烦是:虽然从观感上看所有的控件似乎是一致的(这得感谢Windows UI设计部门的执行能力),但是如果用一些Spy++/AccExplorer之类的工具一检查就原形毕露了。最经典的例子是XP Explorer的bookmark下拉菜单,它和同一个菜单条里所有其它的下拉菜单都不一样,因为它是为了和IE保持同步而特制的,不知出于什么原因,它的Accessility支持可以说是一团糟,用自动化工具访问菜单栏的时候都必须专门写一些代码才能顺利访问。

    反过来,GTK+那种一开始就独立的项目就不同。开始时它只有一个控件来源,就是Gimp。所有其它的项目如果需要某个新的功能都要有他们的实现,然后提请进入GTK+ Upstream,然后GTK+项目组负责决定是接纳进来还是自己写一个。虽然这样的坏处是有一部分控件被重复设计了,比如libGlade和Gtk-builder,但是好处也是明显的,整个控件集合在扩展的时候能保持一致的设计风格,而且没有引起不必要的外部依赖。

    至于Mac和Adobe的情况,俺没在那边干过不便置喙。有人解释一下否?

    P.S.:现在 Gnome也开始大量地port控件到GTK+,老实说俺很不喜欢,因为他们的port没有删除对Gnome的依赖,导致一些新一些的空间需要反过来依赖Gnome。但这就是另一个故事了。

sipoint 发表评论 取消回复