DZone
Java Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Java Zone > Series: DSLs in Groovy, Part: 3

Series: DSLs in Groovy, Part: 3

Victor Savkin user avatar by
Victor Savkin
·
Feb. 21, 11 · Java Zone · Interview
Like (0)
Save
Tweet
6.39K Views

Join the DZone community and get the full member experience.

Join For Free

This is the third post indented to show how easy it is to write domain specific languages in Groovy. For better understanding of this one you’d better read the first two posts:

  • Series: DSLs in Groovy, Part: 1

  • Series: DSLs in Groovy, Part: 2

In my first post I took a very simple example of a DSL: Twitter followers graph. The result was fine but not very impressive:

List<User> users = FollowersGraphBuilder.build {
    users 'Jim', 'Tom', 'Jane'
    user('Jim').follows('Tom').and('Jane')
    user('Tom').follows 'Jane'
}

Semantic Model, Object Scoping, Symbol Table, Expression Builder, Method Chain patterns were used to write this DSL. To learn more about these patterns I recommend you to buy an excellent book by Martin Fowler about domain specific languages.

The pattern Dynamic Reception allowed me to improve the DSL in the second post:

List<User> users = FollowersGraphBuilder.build {
    users Jim, Tom, Jane
    Jim.follows Tom and Jane
    Tom.follows Jane
}

What I am going to do in this post is to show how to achieve similar results by using totally different approach. I’d say that it is a statically-typed approach.

First of all, let’s look at our semantic model. It remains the same:

class User {
    private String name
    final followers = [] as Set
    final following = [] as Set

    User(String name) {
        this.name = name
    }

    void addFollower(User u) {
        followers << u
        u.following << this
    }

    boolean equals(obj) {
        getClass() == obj?.getClass() && name == obj?.name
    }
}

I have a user having two sets of other users and a name. It is so simple. As with all the previous examples I’m gonna use Object Scoping to provide all the methods used in my DSL. In all the previous examples I used delegate property of a closure, but not this time. The way I’m gonna do it this time is by using inheritance.

I’ll write AbstractFollowersGraphBuilder that has to be extended by all builders. The result I’d like to get is:

class FollowersGraphBuilder extends AbstractFollowersGraphBuilder {
    Users Jim, Tom, Jane

    void build (){
        Jim.follows Tom and Jane 
        Tom.follows Jane
    }
}

List<User> users = builder.createUsers()

Note: Jim.follows Tom and Jane will work only if you use Groovy 1.8. If you are still using the obsolete Groovy 1.7 you will have to do it this way: Jim.follows(Tom).and(Jane).

The first question you might have is what is Users? In all the previous examples this class was called UserFollowingBuilder:

class Users {
    User user

    def follows(Users users) {
        users.user.addFollower user
        this
    }

    def and(Users users) {
        follows users
    }
}

I renamed it because in the previous examples a client was not aware of the existence of UserFollowingBuilder. In our current DSL the situation is different. We have to explicitly specify this class. And for me:

Users Jim, Tom, Jane

looks a way better than:

UserFollowingBuilder Jim, Tom, Jane

Let’s take a look at AbstractFollowersGraphBuilder:

abstract class AbstractFollowersGraphBuilder {

    abstract void build()

    List<User> createUsers(){
        initUserFields()
        build()
        return allUserFields()
    }

    private initUserFields(){
        getClass().declaredFields.toList().findAll{it.type == Users}.each{Field f->
            f.accessible = true
            f.set this, new Users(user: new User(f.name))
        }
    }

    private allUserFields(){
        List<Users> users = getClass().declaredFields.toList().findAll{it.type == Users}.collect{Field f->
            f.accessible = true
            f.get this
        }
        users.user
    }
}

First of all, I’m trying to find all the fields of Users type. After that I’m creating all required builders (Users is a builder) and assigning them to the fields. So when build is invoked all the builders will be created. build method must be implemented by a subclass where all the connections between users will be established.

And the result is:

class FollowersGraphBuilder extends AbstractFollowersGraphBuilder {
    Users Jim, Tom, Jane

    void build (){
        Jim.follows Tom and Jane
        Tom.follows Jane
    }
}

def builder = new FollowersGraphBuilder()
List<User> users = builder.createUsers()

We’ve created a special builder for our semantic model - Users. In real life examples where you have a few classes in your semantic model you’d probably have to create a builder per class. Each object that is used in our DSL is defined as a field and our AbstractFollowersGraphBuilder will take care of creating a right builder for each field. The pattern that has been used here is called Class Symbol Table. The class itself is a symbol table and the fields of the class are records.

It’s a really cool technique as it can be used with such languages as Java or C#. It also gives you autocompletion and refactoring. On the other hand you have to create a new class each time you want to use your DSL.

From http://victorsavkin.com/post/3383696210/series-dsls-in-groovy-part-3

Groovy (programming language) Database

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • The Evolution of Configuration Management: IaC vs. GitOps
  • The Right Way to Hybridize Your Product Development Technique
  • Maven Tutorial: Nice and Easy [Video]
  • How to Leverage Method Chaining To Add Smart Message Routing in Java

Comments

Java Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo