Saturday, January 14, 2012

With Groovy comes great power of DSL(Domain Specific Language)

Slowly and steadily I have started realizing the power of DSL(Domain Specific Langauge) and I found Groovy a very powerful tool to write DSL. DSL takes programming to next level by allowing  input to a program be given as set of actions than merely a function call.If you want to explore DSL in detail, I would recommend you to read Martin Fowler's book on DSL.


Closures play an important role while writing DSL in groovy. In Grails, DSL with closure has been used extensively. Take an example of GORM Criteria.

def criteria = Person.createCriteria()
def result = criteria.list{
    like("name","%XYZ%")
    eq("gender","male")
    gt("age",30)
}

In above example we have passed closure to list method of criteria to get all Persons whose name matches "XYZ", gender is equal to male and age is greater than 30. Keywords like, eq and gt are very intuitive and anybody reading the code can tell what code is expected to do just by looking at it.

In my project also I have started using DSL at various places which I think are ideal places for DSL. Take an example of Email Service. The sendMail method of emailService expects certain data to be provided as input e.g. from, to, cc, bcc, subject etc.

*Note : This implementation is similar to one provided by MailService plugin in grails.

emailService.sendMail{
    from    admin@xyz.com
    to to@xyz.com
    cc cc@xyz.com
    bcc bcc@xyz.com
    subject Test
}


Behind the scene in EmailService I assign delegate of closure as EmailData instance. With this it is now possible to map to,cc,bcc,subject to methods like
void to(String to)
void cc(String cc)
and so on...Within these methods, the data is assigned to instance variables like to, cc, bcc. This data can then be used in actual logic to send mail.
class EmailService{

    void sendMail(Closure closure){
        EmailData emailData = new EmailData()
        closure.delegate = emailData
        closure.resolveStrategy = Closure.DELEGATE_FIRST
        closure.call()
        /*
        Write the logic here to send email. Required data can be
        accessed as ..
            emailData.to
            emailData.cc
            emailData.bcc
            emailData.subject
        */
    }
    
    class EmailData{
        String to
        String cc
        String bcc
        String subject
        
        void to(String to){
            this.to=to
        }
        
        void cc(String cc){
            this.cc=cc
        }
        
        void bcc(String bcc){
            this.bcc=bcc
        }
        
        void subject(String subject){
            this.subject=subject
        }
    }

}


This way, DSL makes code more readable and intuitive. Client of EmailService API do not need to create an instance of EmailData and set required variables. They can simply pass closure to sendMail method and can pass required data as action.

 I have personally started liking DSL and will be using more and more going forward. Do let me know your thoughts/comments on this.

No comments:

Post a Comment