没有NIB文件的Cocoa应用是可行的,但是非常困难。⋯⋯在开始接触Cocoa的20年间,我几乎每过3个月就会看到有人讨论这样的问题。
三个阵营欢迎你的加入:
1. IB一开始是令人痛苦的,但是你会渐渐离不开它。
2. IB起码聊胜于无,不要白不要。
3. IB有利有弊,逆来顺受吧。
上面是从一次关于Cocoa XIB(以前称为NIB)的长篇邮件列表讨论中摘出来的两段。尽管这个题为《Writing Cocoa apps w/o using Interface Builder》的问题错误的发到了Xcode列表(本该是Cocoa-dev列表),而且其间也不断的有人指出这一点,但是没有阻止每个回信者(包括第一句就指出问题发错了地方的)都慷慨的发表对XIB的看法。最终有30人次发言,不说Xcode列表本来就没有Cocoa-dev热闹,即使是Cocoa-dev列表也少有这么吸引人的话题。这种情景让我想到专用语言(domain-specific language)在界面设计中应该如何应用仍然是一个未有定论的话题。
我的早期接触
我接触的第一种DSL是Windows的资源文件(.rc文件)。当时看来——现在也多少如此——在纯粹的C/C++环境中引入一个语言异类把开发过程搞得非常难以理解,令人厌恶而恐惧。直到发现资源文件描述的东西用C API也统统能实现,对它的恐惧心理稍减,厌恶感更甚。这种DSL简直是毫无用处,多此一举。
除了SQL和IDL之外,我接触的第二种DSL是北电内部开发的一种把一种类型的Java对象转换成另一种Java对象的语言。当时的研发中心还不正式属于北电,开发过程也不太严格,程序员可以对很多事情自作主张。在发现Java的代码也能提供这个DSL的功能之后,我马上下定决心不使用这个DSL(因为没有办法完全绕过,最终写了一个非常小的不做任何转换的DSL文件)。结果是非常令我满意的,那些用了这个DSL的同事除了无穷无尽的bug,什么也没得到,因为这个DSL不光需要编译,而且要先编译成Java代码,再编译成bytecode,既失去了调试所需的透明度,又没有节省任何编译时间。
这就是DSL在我的早期开发生活中给我留下的印象。严格的说,引起我反感的不是全部的DSL,仅仅是具有下面特征的:首先,这种DSL是声明式(declarative)语言,而不是命令式的(imperative)。它们不是图灵完备的,只能根据预先定义的一些规则(rules)来粗糙的描述使用者要达到的目的,而无法精细的控制每一步(step);其次,它们的所有功能,至少是绝大部分功能,都能被某种命令式语言替代。比如Windows资源文件的功能能够被直接调用Win32 API的C代码完全替代。从这个方面来说,我并不反感像SQL或者IDL这样的DSL。因为它们是为了完成命令式语言无法很好完成的工作而设计的独有接口,并非仅仅是命令式语言的某种速写符。
在接触DSL很久之后才了解DSL,decalratvie和imperative这样的名词。一开始的概念就是“只有程序代码最好”。
DSL与UI
Java Swing是众多UI framework里面完全不使用DSL的一个罕见的例子。这是Sun追求100%纯Java的副作用。这样做的好处在于程序员不用再学习专门的DSL。 程序的编译和分发也更加简单统一。带来的问题是定义UI的代码十分冗长。特别是命令式语言和UI定义的对应方式不直观,这样用可视化工具产生的UI代码除 了本身比手工编写的代码更为冗长之外,每种可视化工具产生代码的方式也不相同。用命令式的步骤来理解产生的UI效果,让程序的可读性很差。
用命令式语言表示UI的问题,也就是引入UI DSL的初衷。尽管有类似北电那种情况的特例,DSL的绝大部分都是在图形界面设计方面的。这是由图形界面设计的本质决定的。UI的布局部分很适合声明式语言。
Windows的资源文件有了20多年的历史。作为UI开发还不为人们熟悉的时代的产物,当它出现的时候,人们把使用它作为一种新奇的技能,还来不及权衡其利弊。20多年之后,资源文件已经很少使用。它的语法和功能都过于陈旧,只能用来偶尔声明一些比较简单的对话框。对于复杂的国际化和本地化需求,资源文件也是疲于应付。资源文件无法提供具有很强动态特性的UI。后来的framwork一般都拥有运行时的自动化布局功能。自动化布局让纯粹代码表示的UI变得勉强可以接受,不致可读性非常差。这也是Java能够完全放弃DSL的一个必要条件。
现代的UI framework基本都使用DSL,大多采用基于XML的语法。比如GTK和Cocoa。这些DSL从本质上说仍然是代码的速记符——完全脱离这些DSL用纯代码实现的UI是理论上可行的。但是就像一开头说的,完全脱离DSL是不建议的,实际上非常困难的。
DSL的利弊
DSL让程序的代码量,尤其是大体类似的冗余代码量大大减少。减少代码量就意味着减少引入错误的机会和减少维护量。但是同时,DSL让程序失去了一部分透明度。由于失去透明度带来的维护开销是否能够被代码量减少带来的收益抵消是一个很难衡量的问题。
DSL对于版本控制也不是很友好。UI DSL一般由可视化工具生成而很少手工编写。通过可视化工具做出的很小的改动往往造成DSL文件的很大变化。这种“雪崩效应”给版本控制和历史改动的跟踪带来了很大的麻烦。两个方面可以解决这个问题。第一,可视化工具尽可能消除“雪崩效应”;第二,可视化工具支持DSL的双向修改,即经过手工修改的合法的DSL文件可以被可视化工具正常操作,这样开发者在做出很小的改动时可以直接手工修改,出现“雪崩效应”时也可以用手工补救。
DSL对于跨平台开发非常不利。跨平台开发使用的语言绝大多数是命令式的。其一般的策略是把各个平台特有的API封装起来,对上提供统一的接口。由于跨平台部分采用命令式语言,接口本身和支持接口的平台特有API也最好是命令式的才能让整个架构的集成更为自然。所以跨平台开发中对于跨平台接口以下层次中DSL的使用总是极力避免。像Cocoa这样的framework并没有成为跨平台的接口,通常作为平台特有的API处于跨平台接口之下,这种情景中对绕过XIB的需求最为强烈。而对于GTK这种本身支持多平台的framework,这种需求就小得多。
DSL适合中等程度UI定制化。对于UI控制度极高的场合,比如游戏设计,UI DSL没有什么市场,整体UI都是用命令式语言编写。对于一些prototype UI,人们往往习惯用命令式语言调用预设定的alert popup或者简单可控的对话框。
对DSL是否利大于弊我仍然持怀疑态度。但是Java Swing遭到了失败(尽管有其它原因),越来越多的framework采用DSL,可视化工具的更多应用,人们不可避免的越来越习惯和无法脱离UI DSL。
2009/06/02 10:56 上午 |
“中等程度的定制化”太含糊,按俺的观点,基于DSL的方案就适合“不需要自己写控件”的场合,理由很简单:a) 控件质量有基本的保证,b) 不会有过于怪异的用法,比如Windows这边最喜欢干的多次重入同一个消息循环之类的。反过来像游戏UI那种基本上要求100%定制的还是免谈了。
至于双向修改,俺倒认为没什么必要关心。只要生成的配置文件是纯文本的,就放心地交给版本控制工具吧。现在的版本控制工具没那么脆弱。另外需要补充一点:双向修改的困难更多地体现在Visual Studio那种UI直接映射到代码转换的部分,因为代码受程序员编码风格的影响变数太大。反过来,基于纯文本描述的方案(XML的和资源文件都属于此类)对文件内容的修改比较小,比如gtk-builder,我的经验里还没有发现因为添加一个子控件导致配置内容全部替换的情况。
2009/06/04 1:16 下午 |
“对DSL是否利大于弊我仍然持怀疑态度”。你真是有怀疑精神,你自己都已经说Java Swing已经失败了,还怀疑什么。
出于精英意识的设计和企图总会失败。过高估计了自己,过低估计了世界的复杂度。
2009/06/04 10:14 下午 |
从wordpress.com反向同步比较困难,所以手工做了:
From zy on 界面开发中的DSL # “对DSL是否利大于弊我仍然持怀疑态度”。你真是有怀疑精神,你自己都已经说Java Swing已经失败了,还怀疑什么。 出于精英意识的设计和企图总会失败。过高估计了自己,过低估计了世界的复杂度。