最近在读一本关于Grails的书时看到以下这句话:
但Grails的目标却大为迥异,它并不是Rails在Groovy语言上的一个移植版本,而是将业界内最为强悍的组件(比如说Spring、Hibernate、Quartz、Compass和SiteMesh等)以最佳方式组合起来的一个实践,并通过采纳无配置规约(Convention-over-Configuration,CoC)使它们符合“不重复(Don't Repeat Yourself,DRY)”原则。……
我对其中“无配置规约”、“不重复”这两点感想颇多。什么是无配置规约?记得前几天看了一个关于百岁老人的访谈节目,其中一对老年夫妇给我留下了深刻的印象。他们都已过百岁,老爷爷耳朵不好,别人给他说话要费很大劲才行。但是如果是老婆婆,哪怕只是很小的声音,老爷爷都能知道她说什么。很多时候,他们之间的交流甚至不需要语言,一个眼神,一个手势就足够了。这是什么?这就是默契,一种只有在特定的感情基础上建立起来的默契。那么把这种默契引伸到IT技术上,就是无配置规约,也就是说不需要多余的配置信息就可以表达相互的约定。既然是无配置规约,当然就避免了不必要的重复,从而大大提高生产率。这当然好,可现实情况又是怎样的呢?
我接触Java算起来也有快10年的时间了,虽然她仍然是我最喜欢的语言,可以说我对她是有感情的,但是到现在为止,我还是感受不到我们之间的默契。还是举个例子来说吧:
这是再普通不过的一段Java代码,可是你有没有发现,其实如果我们之间多一点默契,就不必写这么多行代码?
首先,为什么要写import java.uti.XXX,熟悉Java的人都知道,java.util包其实是使用频率非常高,且不说按Best Practics,为每个类都要写import,就是只写的import java.util.*,那么如果在大型的项目中这个工作量也不小。为什么我们就不能有一点默契,由Java帮我分析代码中用了哪些类,帮我import?
for ( ;; )这个语法用得太普遍了,为什么一定要int i?既然90%的代码都是步长为1的循环为什么就不能写成for( i in 0..9 ),当然JDK5已学加入了for ( x in y )语法,也算是一个进步吧,但对于数字的处理还是略显笨重
为什么要分号?没有它不也一样可以表达我的意思吗?当然将两行语句写在一行是需要分号隔开。
为什么要使用class Erase定义类文件?既然大部分情况下一个文件只写一个类,并且类名等于文件名,那就没必要写class Erase。当然,有一种情况也需需要注意就是类前面有时需要加public等修饰符。但我想大部分会是public,为什么就不能多一点默契?
还有就是最让人生厌的get/set
…………
只是上面的代码要表达的意思无非是:
names = ["Ted", "Fred", "Jed", "Ned"]
println names
shortNames= names.findAll{ it.size() <= 3 }
printlnshortNames.size()
shortNames.each{ printlnit } 细心的读者也许已学看出,以上是Groovy的语法,虽然其它的语言也许或多或少的具有以上优势,但Groovy是运行在JVM之上,可以兼容和使用Java类库,因此我们以前的Java知识可以完全用得上,这一点上是其它语言所无法比拟的。还有一个重要的特点就是Groovy引擎会将Groovy文件编译成.class来执行,也就是说Groovy并非传统意义上的解释执行。这些都是题外话,但不管怎么说,Groovy确实让我们感觉上更加默契一点、贴心一点。
J2EE方面也是同样问题,就拿其中的Web来说吧,其实无论使用什么框架Struts也好、Webwork也罢其实要实现的功能无非是:接收用户请求->解析请求->调用服务层->调用持久层->计算结果->返回给用户(包括验证结果等)。但是当你工作时,你就会觉得写起Web应用象是给一位老人对话,你不停得写着各种各样的与置文件,写各种各样的Action,验证逻辑,很多时候同一个逻辑要反复写很多配置文件或辅助类。就拿比较先进的Beehive(基于Struts)架框来说:
import org.apache.beehive.netui.pageflow.Forward;
import org.apache.beehive.netui.pageflow.PageFlowController;
import org.apache.beehive.netui.pageflow.annotations.Jpf;
@Jpf.Controller(
simpleActions={
@Jpf.SimpleAction(name="begin", path="index.jsp")
},
sharedFlowRefs={
@Jpf.SharedFlowRef(name="shared", type=shared.SharedFlow.class)
}
)
public class Controller
extends PageFlowController
{
@Jpf.SharedFlowField(name="shared")
private shared.SharedFlow sharedFlow;
@Control()
protected LoansDBControl myLoansControl;
/**
* Action to drop the table
*/
@Jpf.Action(
forwards = {
@Jpf.Forward( name="success", path="output.jsp",
actionOutputs = {
@Jpf.ActionOutput(name = "actionOutput",
type = java.lang.String.class )
}
)
}
)
………
上面只是部分代码,我想如果你不会Beehive的话,单单从代码上你很难看出其中的逻辑关系,这里面用了太多的Annotation,虽然相对于编写置文件还是改进了很多,但问题是这些Annotation表达的意思是否就是最简化呢?例如:既然用方法来包装Action处理,为什么还要写那么多Annotation注明一下?为什么不能让方法名和要返回的页面名子一样,以省掉配置大量的Forward?为什么不能将业务Model放入一个Map就完事儿,还要写那么多Annotation详细注明?当然,问题还有很多,但是我们已经感受到,这里同样缺乏应用的默契,让人难以亲近。那么同样的问题有没有更好的解决办法?我们来看一个Grails的例子:
class UserController {
def index = { redirect(action:list,params:params) }
def allowedMethods = [delete:'POST', delete:'GET', handleRegistration:'POST', update:'POST']
def login = {
session.fromLogin = true
}
def logout = {
session.user = null
session.fromLogin = null
redirect( controller:'user', action:'login' )
}
def handleLogin = {
def user = User.findWhere( username:params[ 'username' ], password:params[ 'password' ] )
if ( user ) {
session.user = user
redirect( controller:'expenseSheet', action:'list' )
}
else {
flash.error = "User ${params.username} does not exist."
redirect( controller:'user', action:'login' )
}
}
def list = {
if(!params.max)params.max = 10
[ userList: User.list( params ) ]
}
def show = {
[ user : User.get( params.id ) ]
}
def delete = {
def user = User.get( params.id )
if(user) {
user.delete()
flash.message = "User ${params.id} deleted."
redirect(action:list)
}
else {
flash.message = "User not found with id ${params.id}"
redirect(action:list)
}
}
……… 我们同样以一个Controller为例,这里你看不到繁琐的Annotation的定义,也没有繁琐的配置信息,单单从代码上就可以看出他要表达的意思。这里我们简单的解释一下:
def index = { redirect(action:list,params:params) } 也就是说index默认页面被重定向到名叫list的Action上,def list = { if(!params.max)params.max = 10 [ userList: User.list( params ) ] } ,params.max设置翻页时每页所显示的记录数,[ userList: User.list( params ) ]这是一个Map,相当于MVC中的Model,也就是说list这个Action执行完后,会带同这个带有用户列表的Map返回到名叫list的页面上显示,相当于MVC中的V。我们再来看个例子:
class ExpenseSheet {
Float amount
String status
String city
String type
String comment
Date beginDate
Date endDate
Date logDate
Date handDate
User user
static constraints = {
amount( scale:2 )
status( inList:['未付','已付'], blank:false )
city( blank:false, notEqual:"请选择城市名" )
type( inList:['出差','出租','日常'], blank:false )
comment( nullable:true, blank:true, size:1..255 )
logDate( min:new Date() )………
上面是一个实体类,以往在处理实体验证时非常繁琐,什么ActionForm、valiate方法了、还有什么验证插件,其实细想起来我们要的是什么?我们如果只看"static constraints={…}”部分,就算你不是技术人员都能看懂个差不多,account是浮点型,小数点后保留2位、status是常量,选值范围是“未付、已付”,并且不可以为空、city不可以为空,并且其值不可以是“请选择城市名”等等。至于具体的难证细节,你不必再操心了,交由Grails处理。
虽然你没有任何Grails经验,但是我简单这么一说,你多少就会理解。因为,整个过程是那么自然,那么默契,这样的应用写起来就象是与一个聪明的小伙伴对话。也许你会认为这样的程序执行效率低下,这点你大可以放心,Grails应用最终会被编译成Spring+Hibernate(还有其它一些成熟的开源项目)的标准J2EE Web应用,可以被部署到任何J2EE Web容器中。
当然,这里举得两个例子只是冰山一角,其实我们工作中还有很多可以通过这种默契来提高工作效率的例子,这也许是人类懒惰的天性,但也不可否认这也是技术进步的源泉。J2EE在开发效率一直落后于.NET也许正是由于忽略了“默契”这个最有效的武器,还好,开源领域正在意识这一点,RoR与Grails也许就是他们中的先行者,不管怎么说,这一步走得还不错。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gary_niu/archive/2007/11/05/1867327.aspx