Groovy高效编程——创建DSL

 
Groovy高效编程——创建DSL
利用Groovy与生俱来的动态特性,创建DSL(Domain Specific Language)是一件十分容易的事情。
下面通过一个例子,向大家展示一下用Groovy创建DSL的优雅之处:

利用下面这种语法
    person {
        name {
            firstname 'Daniel'
            lastname  'Sun'
        }
    }

    person {
        name {
            firstname = '山风'
            lastname  = '小子'
        }
    }
创建一个Person对象。

def createMetaClass(Class clazz, Closure closure) {
    
/*  
        为传入的Class对象创建一个ExpandoMetaClass实例,但不将该ExpandoMetaClass实例注册到MetaClassRegistry对象中
    
*/
    def emc 
=   new  ExpandoMetaClass(clazz,  false
    
/*
        该closure用来初始化ExpandoMetaClass对象,这种写法的思想与Template Method Pattern有异曲同工之妙
    
*/
    closure(emc) 
    emc.initialize() 
//  完成初始化过程
     return  emc
}

def executeScript(dslScriptCode, rootName, closure) {
    Script dslScript 
=   new  GroovyShell().parse(dslScriptCode)   //  读取并解析DSL代码,返回一个Script对象
    
    dslScript.metaClass 
=  createMetaClass(dslScript. class ) { emc  ->
    
/*
        动态新增一个名为"$rootName"的方法,注意"$rootName"的值决定于运行时,比如本例中的值为person
    
*/
        emc.
" $rootName "   =  closure 
    }
    
    
return  dslScript.run()  //  执行DSL代码
}

class  Name {
    String firstname
    String lastname
    String toString() {
        
" $firstname.$lastname "
    }
}

class  Person {
    Name name
    Person(name) {
        
this .name  =  name
    }
    String toString() {
        
" My name is $name "
    }
}

/*
PersonDelegate对象是下面作为参数传入‘person方法’的closure的delegate,形象点说,closure就是那对大括号{}以及大括号中的内容
如果您对closure的delegate不太熟悉,可以参考在下的另一篇文章《Groovy解惑——closure中的delegate》(
http://www.blogjava.net/BlueSUN/archive/2007/12/22/169580.html )
    person {
        dot.gif
    }
*/
class  PersonDelegate {
    def person
    PersonDelegate(person) {
        
this .person  =  person
    }
    
/*  
        关于methodMissing这一特殊方法,请参考在下的另一篇文章《Groovy高效编程——动态改变对象的能力》(
http://www.blogjava.net/BlueSUN/archive/2007/07/15/130318.html )
    
*/
    def methodMissing(String name, Object args) {
        
if  ( ' name '   ==  name  &&  args[ 0 instanceof  Closure) {
            def nameClosure 
=  args[ 0 ]
            
/*  
                给nameClosure的delegate赋值,nameClosure就是name旁边的那个closure即一对大括号{}以及大括号中的内容
            
*/
            nameClosure.delegate 
=   new  NameDelegate(person)  
            nameClosure.resolveStrategy 
=  Closure.DELEGATE_FIRST  //  指明closure中变量和方法的解析策略,本例选择DELEGATE_FIRST
            nameClosure()
        }
    }
    
/*  
        关于propertyMissing这一特殊方法,请参考在下的另一篇文章《Groovy高效编程——动态改变对象的能力》(
http://www.blogjava.net/BlueSUN/archive/2007/07/15/130318.html )
    
*/
    def propertyMissing(String name) {}
}

/*
类似于PersonDelegate,
NameDelegate对象是下面作为参数传入‘name方法’的closure的delegate
        name {
            dot.gif
        }
*/
class  NameDelegate {
    def person
    NameDelegate(person) {
        
this .person  =  person
    }
/*
    下面这些getter和setter是为了实现下面这种赋值而写的: firstname = '山风'和lastname  = '小子'
    person {
        name {
            firstname = '山风'
            lastname  = '小子'
        }
    }
*/
    def getFirstname() {
        
return  person.name.firstname
    }
    def setFirstname(String firstname) {
        person.name.firstname 
=  firstname
    }
    def getLastname() {
        
return  person.name.lastname
    }
    def setLastname(String lastname) {
        person.name.lastname 
=  lastname
    }
    
    def methodMissing(String name, Object args) {
        
if  ( ' firstname '   ==  name) {
            person.name.firstname 
=  args[ 0 ]
        } 
else   if  ( ' lastname '   ==  name) {
            person.name.lastname 
=  args[ 0 ]
        }
    }
    def propertyMissing(String name) {}
}

/*
    在这篇文章中,演示了两种赋值方式,各位可以根据自己的喜好选择一种,我个人偏好第一种 :)
*/

//  本例DSL的第一种写法
def dslScriptCode  =   '''
    person {
        name {
            firstname 
' Daniel '
            lastname  
' Sun '
        }
    }
'''

def scriptClosure 
=  { Closure personClosure  ->
    def person 
=   new  Person( new  Name())
    personClosure.delegate 
=   new  PersonDelegate(person)
    personClosure.resolveStrategy 
=  Closure.DELEGATE_FIRST
    personClosure()
    
    
return  person
}
def person 
=  executeScript(dslScriptCode,  ' person ' , scriptClosure)

println person


//  本例DSL的第二种写法
def dslScriptCode2  =   '''
    person {
        name {
            firstname 
=   ' 山风 '
            lastname  
=   ' 小子 '
        }
    }
'''

def scriptClosure2 
=  { Closure personClosure  ->
    def person2 
=   new  Person( new  Name())
    personClosure.delegate 
=   new  PersonDelegate(person2)
    personClosure.resolveStrategy 
=  Closure.DELEGATE_FIRST
    personClosure()
    
    
return  person2
}
def person2 
=  executeScript(dslScriptCode2,  ' person ' , scriptClosure2)

println person2


运行结果:
My name is Daniel.Sun
My name is 山风.小子

你可能感兴趣的:(Groovy)