论坛首页 Java版 Webwork

显示逻辑AOP, 复杂页面逻辑Craker -- fastm. .....

浏览 29431 次
该帖已经被评为精华帖
作者 正文
时间:2004-07-19
这段时间在javaeye的关于fastm的讨论,令我受益良多。特此感谢大家。非常感谢。
很多人鼓励我的精神,很多人耐心指正我的错误,也有很多人提出尖锐深刻的问题。最感谢的是,甚至有些朋友深入研究了fastm,并提出了改进方案。
并且很多朋友也讨论了fastm之外的话题,令我的知识面开阔。

我这里把这几天针对fastm的一些尖锐深刻的问题列出来。并感谢大家对fastm的深入思考、详细讨论和尖锐抨击。

(1)格式化信息分布在Template DOM和ValueSet DOM两个地方
charon 写道
还有关键的一点,即便按你所说得这么做是正常的,那么会有一个非常严重的不一致现象,或者一种臭味:有一些格式化的信息是在你的模板中的,另一些格式化信息则是你的"model"自带的。好好想一想开发哲学吧。


这个问题最深刻尖锐,击中fastm的痛处。
如果需要动态改变风格,确实需要在ValueSet DOM中设置风格。
这个问题是fastm无法解决的,因为fastm模板中不包含逻辑。

下面是我的辩护:
这是把逻辑从模板中驱逐出去必须付出的代价。
比起逻辑代码分布在模板和Java Code两个地方,我觉得,格式化信息分布在Template DOM和ValueSet DOM两个地方,这个代价还是值得的。
这个问题就是见仁见智,各有千秋了。

并且,在代码中改变界面风格,这种方式在有界面资源的桌面程序中很常见。
资源文件中存放着基本的界面控件信息。程序可以动态地改变资源文件的显示风格。
所以,我觉得,“在代码中改变界面风格”,也不是完全不可接受的。

(2)fastm的所见即所得并不是绝对的。

我在论坛里翻了半天,也没有找到是哪个朋友提出的这个尖锐深刻的问题。Sorry。
当fastm需要选择显示不同的内容的时候,模板里面同样要包括两块内容。
这样你在浏览器中看的时候,可以看到两块内容。
而程序运行的时候,却只出现一块内容。

所以,fastm显示的所见即所得也不是绝对的。

(3)违反了MVC
这个问题也确实存在。
fastm不是不能支持MVC,只是支持了MVC之后,操作不够直接。
fastm只和Servlet Response打交道就够了。
Template DOM + ValueSet DOM = result --> Response.
(4)DOM的复杂性。

其实,这个问题有些误会。fastm的DOM不是HTML DOM。没有那么复杂。

页面逻辑的嵌套层次有多少,那么fastm DOM的层次深度就有多少。
比如,这样的JSP代码
[code:1]
<c:choose>
<c:when test="${copyMade}">
here we add a line.
<h4><fmt:message key="copy.ok"/></h4>
</c:when>
<c:otherwise>
here we add a line too.
<h4><fmt:message key="copy.nok"/></h4>
</c:otherwise>
</c:choose>
[/code:1]

因为逻辑嵌套只有两层,那么对应的fastm DOM也只有两层。
fastm DOM其实没有增加复杂度。而且,fastm DOM还简化了复杂度。
这是fastm的一个优点。
ValueSet DOM是fastm实现显示逻辑AOP和显示逻辑重用的关键结构。
也是Pipeline式处理过程的数据交换中枢。

不在这里讨论了。:-) 以后证明。

(5)fastm的代码长

其实,这个问题也有些误会。
只是“看起来”,fastm的代码比较长。
我本来是把fastm代码短作为一个优势来宣传的。

其它模板技术的代码分布在模板和Java Code两端。
即使不计算TagLib的代码。fastm代码其实还要比其它的模板技术短。

而且,fastm代码只存在于Java Code里,而且很多都是公用方法 -- 页面逻辑AOP。

不在这里讨论了。:-) 以后证明。
   
时间:2004-07-19
呵呵,下面是我问大家的话。希望不要介意。

以前我从来没有意料到,大家对模板脚本如此的执著和热烈。
“和显示相关的逻辑只应该存在于模板的脚本中,不应该存在于Java Code里面。”

fastm把显示相关的逻辑移到了Java Code里面。
大家一看到,fastm触犯了这个“只应该”和“不应该”的绝对真理,就开始猛烈批判,说这是一种“倒退”。
既然已经定性为“倒退”,于是就不愿意更深入的去理解fastm真正解决了什么问题。
我想,由于这个先入之见,大家其实对fastm的根本思想都没有、也不愿真正的了解。

我很欢迎大家的鼓励、讨论、改进、指正、批判和抨击。
但我也请求大家,能不能先暂时把这个“只应该”和“不应该”的绝对真理放在一边,真正理解一下fastm所真正解决的问题?

下面我尽量举个直观而效果明显的例子。
假设我们有如下的模板脚本:

[code:1]

for each person in List
<… html text ….>
person.name
<… html text ….>
if(person.gender == 1) {
<…. html text ….>
male
<… html text ….>
}else if(person.gender == 0){
<…. html text ….>
female
<… html text ….>
}
<….html… text>
… other fields
<….html… text>

if(person.gender == 1) {
<…. html text ….>
favorite sports
<… html text ….>
}else if(person.gender == 0){
<…. html text ….>
favorite perfume
<… html text ….>
}
end for;
[/code:1]

可以看到,这个模板里面的逻辑部分和HTML是夹杂在一起的。
[code:1]
for each person in List
if(person.gender == 1) {
}else if(person.gender == 0){
}

if(person.gender == 1) {
}else if(person.gender == 0){
}
end for;
[/code:1]

这部分逻辑(for和里面的if, else)是没有办法重用的。
如果我们需要多种显示的风格和布局。
比如说, List显示风格,Table显示风格,甚至HTML风格,WML风格等。

这个时候JSP,Velocity等脚本就无能为力了。
你必须把这段混杂在HTML中的脚本挑出来,填写到其他风格里面的HTML的对应位置。
你可以看到,同样的显示逻辑,分布在不同的模板文件中,却无法重用。

有人说,XSLT能够解决这个问题。但XSLT也不能。
因为不同的风格,你仍然需要不同的XSL文件。显示逻辑只是移到了XSL文件中而已。同样的显示逻辑,分布在不同的XSL文件里,却无法重用。

真正解决这个问题的,只有fastm。(连XMLC都做不到)
这种情况下,fastm的Template有多少份都没有关系。但ValueSet DOM的处理代码只有一份。页面处理逻辑只有一份。 页面处理逻辑能够达到重用。
而且更简单,比如上面的逻辑中如果用到两次同样的
[code:1]
if(person.gender == 1) {
}else if(person.gender == 0){
}
[/code:1]

在fastm的Java Code中,只要写一次就够了。

如此全面的代码逻辑重用性,关键原因只有一个:fastm把显示逻辑移到Java Code里面了。

这就是fastm的精意所在:重用所有的逻辑,包括显示逻辑。
其它所有的模板技术都做不到这一点,而fastm天生就能够简单的做到这一点。
正是以为这一点,我才确信,fastm能够和所有其它的模板技术竞争。

大家觉得这种 数据逻辑 和 模板的彻底分离 的思路不好吗?
请参见附件里的示意图。

很多人却都执著的认为,View就是“模板 + 显示逻辑”。显示逻辑就是要和模板放在一起才是正确的。否则就是混淆了 View显示层 和 Model层的分离。

这种根深蒂固的“MVC”思路,是fastm目前最大的挑战。
fastm的概念体系里面,没有View显示层 和 Model层的概念,而更象桌面程序的概念,只有页面组件(Template DOM)和页面组件Model(ValueSet DOM)的概念。思路更接近于Swing,而不是MVC。

---- 不应该,不应该,-----

这里,fastm的用法更简单,只有一种页面组件Template DOM和显示Model -- ValueSet DOM。只是恰好有些象页面组件的用法,而且根本没有刻意模仿。
但稍微一提到Swing的类比,就遭到了猛烈抨击——“桌面端的开发方式怎么能用在Web开发里面呢?这是不应该的”。
这又是一个“不应该”。

而市面上存在着大量的Display TagLib。这些TagLib都在Java Code里面输出HTML。但大家都接受的很好。
fastm在Java Code中设置显示风格或格式,却不能被接受。
其实,如果牺牲“所见即所得”,fastm可以完全避免在Java Code中设置显示风格或格式。但为了更好的“所见即所得”,我推荐在Java Code中设置显示风格或格式。

JSP需要request.attribute传输数据。Velocity需要Context传输数据。大家都接受的很好。
fastm需要ValueSet DOM传输数据。却不能被接受。

大家反对fastm的理由主要是,“显示逻辑只应该存在于模板的脚本中,不应该存在于其他地方(比如Java Code)”。

这个“只应该”和“不应该”,让我感到无能为力。
我只能想,
模板脚本什么时候出现的,出现之前是什么情况?
什么时候模板里面加入逻辑脚本成为了“应该”?
什么时候,显示逻辑成为了模板脚本的“只应该”专利,而其他方式都是“不应该”的?
如果这个“只应该”和“不应该”是绝对真理,那我确实无法为fastm辩护了。
  • C0c1a5f4-3066-45e0-bd4f-f95f85d6ac57-thumb
  • 描述: 以上表明了相同数据,多种显示风格的情况下,各种模板技术的表现。 我们可以看到,只有fastm才能够重用所有的逻辑,包括显示逻辑。 其它的模板技术都必须在所有的模板中复制相同的逻辑。
  • 大小: 11.1 KB
   
0 请登录后投票
时间:2004-07-19
标题用中文好点吧, 大家都是中国人,何必呢,如有冒犯之处, 敬请原谅
   
0 请登录后投票
时间:2004-07-19
hehe,我曾经提到过当if/else/foreach很多的情形下,对应的fastm页面静态显示出来的页面未必是用户所期望的,而java代码却是复杂了很多。
本来这就是一个矛盾,如果显示逻辑复杂,怎么能够在静态页面中把可能的情况甚至是主要的几种情形很好的显示出来?
如果有后端程序的配合,比如model自然按照数据树组织,则fastm还是有点意思(至少可以不去生造ValueSet)。不过我也倾向于Quake Wang的看法,数据树还是数据树,找一个脚本来干显示相关的逻辑。模板和脚本分开,就可以分别变化,并且能够动态装配。
   
0 请登录后投票
时间:2004-07-19
对于这个例子,可以把和性别相关的部分拆成更细的单元,就可以重用了。
导航也不必要用 if/else的方式,直接设置一个两元素的数组$gender=("male.vm","female.vm"),然后 包括进 $gender.get(person.gender)这个页面.
这样看起来也整洁得多。
而且这种拆细的要灵活很多。做的工作比java端也要简单。
   
0 请登录后投票
时间:2004-07-19
charon 写道
hehe,我曾经提到过当if/else/foreach很多的情形下,对应的fastm页面静态显示出来的页面未必是用户所期望的,而java代码却是复杂了很多。
本来这就是一个矛盾,如果显示逻辑复杂,怎么能够在静态页面中把可能的情况甚至是主要的几种情形很好的显示出来?
如果有后端程序的配合,比如model自然按照数据树组织,则fastm还是有点意思(至少可以不去生造ValueSet)。不过我也倾向于Quake Wang的看法,数据树还是数据树,找一个脚本来干显示相关的逻辑。模板和脚本分开,就可以分别变化,并且能够动态装配。


其实fastm显示逻辑的Java Code的结构并不复杂。和脚本逻辑的复杂度一样。
语句方面,每个逻辑层次多出了new ArrayList(), add()等几句。
加上减少的request.setAttribute(),还是可以大体持平。
当重复的 if-else 多的时候,fastm的Java Code可能还要少一些。
如果抽出公用方法,代码就更少了。

但是,多谢这些批评。我根据这些批评,还有庄表伟的一些思考。
对fastm进行了改进。提供了一些更方便的方法。
stable版本发布的时候,会加入这些新特性。

1.
List ValueSet.makeValueSets(dyn_name);
这个函数帮助用户自动寻找建立在ValueSet建立一个dyn_name的ValueSet List.
节省了以前的 new ArrayList() 和 setValueSets()。
当然,以前的方法同样支持。

2.
ValueSet.setVariables(bean, propertyNames[]);
这个函数利用reflection把bean的属性一次设置到ValueSet中。

比如,
static final String[] propertyNames = {"name", "score", .....};

valueSet.setVariables(bean, propertyNames);

这样节省了很多行代码。

3. 提供了HTML Tag 内部的attriubte格式的 Dynamic 定义。
比如
<option
BEGIN-DYANMIC="this_one"
selected
END-DYANMIC="this_one"
>

或者,
<span
BEGIN-DYANMIC="this_one"
color = "red"
END-DYANMIC="this_one"
>

能够帮助控制HTML Tag Attribute(style, color)级别的动态选取。

4. 为了处理多种语言的文件,Parser中加入了一个charsetName option.
default charset = 'utf-8'.

5. Parsing出错信息更加友好。行号,错误原因等。
   
0 请登录后投票
时间:2004-07-19
[code:1]if(person.gender == 1) {
}else if(person.gender == 0){
}[/code:1]

象这样的逻辑实际上是业务逻辑,或者叫领域模型。
比较好的作法是把领域部分放在一层中,对应一般就是后台处理。

所以说究竟什么是显示逻辑也是需要好好构筑的,不能认为简单逻辑就无所谓。就好比初拿对象编程的人,会把显示部分做成一个对象的方法,然后调用一下,就一古脑全出来了。开始感觉很爽,把工作分块了,多么清晰,实际上是一种误用。
   
0 请登录后投票
时间:2004-07-20
确实,male.female各一个页面在语义上牢靠一点。在这里也非常优雅。
不过有时候我也有一点迷惑,主要是偷懒引起的:
1. 如果严格按照语义来分,对于复杂一点的页面会出现组合爆炸的情况。比如根据性别、年龄组、爱好等等。为了偷懒,就全在view层干了
2. 当然,这么做是有一个前提的。数据还是那些数据,写在页面里面的是根据数据进行推导的规则,页面所多出来的那些东西实际上是在原始数据基础上根据这些规则搞出来的。但这个规则的度确实不太好把握。哪些规则可以放在view层来搞。hehe,凭直觉了。
   
0 请登录后投票
时间:2004-07-20
如果大家先不去执著于争论 fastm的离经叛道,深入思考,其实可以想到fastm的ValueSet DOM带来的巨大好处-- 我们可以使用AOP批量解决大量的显示逻辑问题,就是我说的通用方法。

1. AOP 格式化
比如,我的工作中有这样的需求。格式化的要求变了几次。
日期格式化,和数字格式化有这样的规律:
结尾为When的属性,比如,createdWhen, updatedWhen,显示到时分秒。
结尾为date的属性,比如,startDate,endDate, 显示到 天。
结尾为period的属性,比如,effectivePeriod,显示到 月。
所有类型为BigDecimal的数据,输出到界面,都需要保留小数点后两位。

如果用Velocity, JSP等,这些格式化代码必然遍布各个模板文件。
对于名字有规律的属性还好说,只要搜索一下,检查所有的模板文件,把日期格式化代码加上就可以了。
但对于数据类型有规律的情况,就没有办法搜索了,必须检查一个个页面,并对应Object的属性定义。这个工作非常的无聊和繁琐。

在fastm中,我用Dynamic Proxy截获了IValueSet.setVariable()方法,
把所有属于这些Pattern的数据,进行统一的格式化。

2. AOP 显示风格
关于基数行、偶数行风格差异的问题。假如这个要求在所有的页面中都一样,那么同样可以用一个AOP Dynamic Proxy统一做这个事情。
或者负数、正数颜色的问题,对于所有页面都是一样的。

我的应用中,由于存在于这样的需求和规律(其实,既然fastm模板的变量名由你自己定,没有规律,你自己也可以创造规律),显示逻辑的代码能够大幅度通用,代码量大幅度减少。
关于代码量,JDK1.5引入了for each等语法,Java Code语句个数进一步减少。
我也在加入了makeValueSets(list beans, propertyNames[])的帮助方法,对于大部分情况,连循环都免了。这一点,脚本怎么做到?
这就是Java Code的巨大优势,你想怎么重用就怎么重用。

我本来希望用户自己根据需要从头写自己的AOP,本来不想在fastm中引入AOP特性,不想让fastm的实现和使用变得复杂。
现在我考虑在fastm项目中引入这个特性。以方便用户,并充分体现fastm的强大。

这是AOP截获的用法。当然,还有其它的用法。
比如,用户甚至可以在整个ValueSet DOM生成之后,再根据需求对ValueSet DOM做任何的后续处理 -- 改变层次,改变变量,改变个数,等等。就相当于Servlet Filter Chain 或 XSLT Pipleline的用法,只是效率更高,速度更快。

3. CSS + JavaScript 处理动态风格 style
至于其它的特殊动态风格要求,CSS + JavaScript就应该能够做到了。
你如果非要用Java Code来设置,也没有什么大碍。看个人的喜好了。

CSS对于改变风格和布局还是很方便的。但你要求CSS替换HTML Tag就勉为其难了。一般来说CSS布局都建立在DIV tag基础上。
但CSS不能把Table换成List,也不能把HTML标签换成WML标签。

而这一点是fastm的强项,fastm天生就是做这个的。你不用把重复的显示逻辑插入到不同 Tag 的模板当中。

而JSP/Velocity从根本机制来说,做不到这一点。CSS也帮不了忙。
但当Tag的区别太大的时候(比如,所有的Tag都从HTML换成WML),View的加强能够进行的一些定制,一些拆细,也无能为力了。
显示逻辑还是要重复的插入到不同的JSP/Velocity文件中。
这是模板脚本的固有的无法克服的局限。

XMLC这种HTML DOM不仅非常厚重低效,而且由于模板和数据的耦合,也根本做不到这一点。想想看,Tag一变,DOM节点的类型和层次都变了,还怎么重用?

这就是我所说的,fastm带来的巨大的变革性。
用最优雅轻巧的结构,实现所有模板结构的重用,实现所有逻辑的重用。

VisualStudio.net for ASP, JSF 等等都是以自动代码生成、结构复杂、代码庞大的代价,带来可视化快速开发。
我一直觉得,这类工具只能用来做玩具级别的东西,不能够满足真正复杂业务、大数据量的高性能要求。

而fastm的思路恰好相反,用简单结构、简化用法的思路带来可视化快速开发。
从根本上解决问题,而不是“修补”问题。

4. 大规模页面应用
另外,fastm的一个重要目标,就是为了大规模的页面系统中应用。
我们有一个系统有几千个JSP页面,如果大量用到现有的标准TagLib,由于TagLib的开销巨大,那么任何一个Web Server(包括Web Logic)根本无法装载和管理几千个庞大的Servlet。这里的问题在于 -- Servlet是属于Web Server管理的资源。
现有的解决办法是,使用自定义TagLib和Java Code。

而fastm却能够轻松的解决这个问题。fastm模板只包含结构,编译速度其快,编译结果很小,几乎和原HTML一样大小,执行速度更快,只有一种操作,匹配ValueSet DOM。

我也研究了Velocity的代码,编译速度也还可以(但包含脚本,比fastm慢),编译结果也不是很大(一个运行节点树,比fastm Template DOM大一些),
执行速度也还可以(比fastm慢一些)。
另外,一般来说,Velocity的解释运行栈的最大空间,比ValueSet DOM要小一些。这也是fastm以结构换逻辑的一点代价。

但我没有在大规模页面中使用Velocity的经验,不知道Velocity的表现如何。
   
0 请登录后投票
时间:2004-07-20
5, 关于 表现层 和 Model的分离

其实,fastm并没有强迫用户不分离。只是没有强制用户 必须分离。
用户完全可以选择自己的结构。

比如,我的应用中,Action做的工作很少,就是从Business或data access层发请求、取得结果数据。代码很短。
那么我就在Action中加了一个方法,把业务数据PO 放到 VO (ValueSet DOM)里面去。

如果你需要把这个方法分离到另一个类,甚至另一个包,完全是用户自己的选择。
   
0 请登录后投票
论坛首页 Java版 Webwork

跳转论坛:
JavaEye推荐