I am the founder and CEO of Data Geekery GmbH, located in Zurich, Switzerland. With our company, we have been selling database products and services around Java and SQL since 2013. Ever since my Master's studies at EPFL in 2006, I have been fascinated by the interaction of Java and SQL. Most of this experience I have obtained in the Swiss E-Banking field through various variants (JDBC, Hibernate, mostly with Oracle). I am happy to share this knowledge at various conferences, JUGs, in-house presentations and on our blog. Lukas is a DZone MVB and is not an employee of DZone and has posted 255 posts at DZone. You can read more from them at their website. View Full User Profile

The Java Fluent API Designer Crash Course

01.16.2012
| 14136 views |
  • submit to reddit

Ever since Martin Fowler’s talks about fluent interfaces, people have started chaining methods all over the place, creating fluent API’s (or DSLs) for every possible use case. In principle, almost every type of DSL can be mapped to Java. Let’s have a look at how this can be done

DSL rules

DSLs (Domain Specific Languages) are usually built up from rules that roughly look like these

1. SINGLE-WORD
2. PARAMETERISED-WORD parameter
3. WORD1 [ OPTIONAL-WORD ]
4. WORD2 { WORD-CHOICE-A | WORD-CHOICE-B }
5. WORD3 [ , WORD3 ... ]

Alternatively, you could also declare your grammar like this (as supported by this nice Railroad Diagrams site):

Grammar ::= (
  'SINGLE-WORD' |
  'PARAMETERISED-WORD' '('[A-Z]+')' |
  'WORD1' 'OPTIONAL-WORD'? |
  'WORD2' ( 'WORD-CHOICE-A' | 'WORD-CHOICE-B' ) |
  'WORD3'+
)

Put in words, you have a start condition or state, from which you can choose some of your languages’ words before reaching an end condition or state. It’s like a state-machine, and can thus be drawn in a picture like this:

Simple Grammar

A simple grammar created with http://railroad.my28msec.com/rr/ui

Java implementation of those rules

With Java interfaces, it is quite simple to model the above DSL. In essence, you have to follow these transformation rules:

  • Every DSL “keyword” becomes a Java method
  • Every DSL “connection” becomes an interface
  • When you have a “mandatory” choice (you can’t skip the next keyword), every keyword of that choice is a method in the current interface. If only one keyword is possible, then there is only one method
  • When you have an “optional” keyword, the current interface extends the next one (with all its keywords / methods)
  • When you have a “repetition” of keywords, the method representing the repeatable keyword returns the interface itself, instead of the next interface
  • Every DSL subdefinition becomes a parameter. This will allow for recursiveness

Note, it is possible to model the above DSL with classes instead of interfaces, as well. But as soon as you want to reuse similar keywords, multiple inheritance of methods may come in very handy and you might just be better off with interfaces.

With these rules set up, you can repeat them at will to create DSLs of arbitrary complexity, like jOOQ. Of course, you’ll have to somehow implement all the interfaces, but that’s another story.

Here’s how the above rules are translated to Java:

// Initial interface, entry point of the DSL
// Depending on your DSL's nature, this can also be a class with static
// methods which can be static imported making your DSL even more fluent
interface Start {
  End singleWord();
  End parameterisedWord(String parameter);
  Intermediate1 word1();
  Intermediate2 word2();
  Intermediate3 word3();
}

// Terminating interface, might also contain methods like execute();
interface End {
  void end();
}

// Intermediate DSL "step" extending the interface that is returned
// by optionalWord(), to make that method "optional"
interface Intermediate1 extends End {
  End optionalWord();
}

// Intermediate DSL "step" providing several choices (similar to Start)
interface Intermediate2 {
  End wordChoiceA();
  End wordChoiceB();
}

// Intermediate interface returning itself on word3(), in order to allow
// for repetitions. Repetitions can be ended any time because this 
// interface extends End
interface Intermediate3 extends End {
  Intermediate3 word3();
}

With the above grammar defined, we can now use this DSL directly in Java. Here are all the possible constructs:

Start start = // ...

start.singleWord().end();
start.parameterisedWord("abc").end();

start.word1().end();
start.word1().optionalWord().end();

start.word2().wordChoiceA().end();
start.word2().wordChoiceB().end();

start.word3().end();
start.word3().word3().end();
start.word3().word3().word3().end();

And the best thing is, your DSL compiles directly in Java! You get a free parser. You can also re-use this DSL in Scala (or Groovy) using the same notation, or a slightly different one in Scala, omitting dots “.” and parentheses “()”:

val start = // ...

(start singleWord) end;
(start parameterisedWord "abc") end;

(start word1) end;
((start word1) optionalWord) end;

((start word2) wordChoiceA) end;
((start word2) wordChoiceB) end;

(start word3) end;
((start word3) word3) end;
(((start word3) word3) word3) end;

Real world examples

Some real world examples can be seen all across the jOOQ documentation and code base. Here’s an extract from a previous post of a rather complex SQL query created with jOOQ:

create().select(
    r1.ROUTINE_NAME,
    r1.SPECIFIC_NAME,
    decode()
        .when(exists(create()
            .selectOne()
            .from(PARAMETERS)
            .where(PARAMETERS.SPECIFIC_SCHEMA.equal(r1.SPECIFIC_SCHEMA))
            .and(PARAMETERS.SPECIFIC_NAME.equal(r1.SPECIFIC_NAME))
            .and(upper(PARAMETERS.PARAMETER_MODE).notEqual("IN"))),
                val("void"))
        .otherwise(r1.DATA_TYPE).as("data_type"),
    r1.NUMERIC_PRECISION,
    r1.NUMERIC_SCALE,
    r1.TYPE_UDT_NAME,
    decode().when(
    exists(
        create().selectOne()
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.notEqual(r1.SPECIFIC_NAME))),
        create().select(count())
            .from(r2)
            .where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
            .and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
            .and(r2.SPECIFIC_NAME.lessOrEqual(r1.SPECIFIC_NAME)).asField())
    .as("overload"))
.from(r1)
.where(r1.ROUTINE_SCHEMA.equal(getSchemaName()))
.orderBy(r1.ROUTINE_NAME.asc())
.fetch()

Here’s another example from a library that looks quite appealing to me. It’s called jRTF and it’s used to create RTF documents in Java in a fluent style:

rtf()
  .header(
    color( 0xff, 0, 0 ).at( 0 ),
    color( 0, 0xff, 0 ).at( 1 ),
    color( 0, 0, 0xff ).at( 2 ),
    font( "Calibri" ).at( 0 ) )
  .section(
        p( font( 1, "Second paragraph" ) ),
        p( color( 1, "green" ) )
  )
).out( out );

Summary

Fluent APIs have been a hype for the last 7 years. Martin Fowler has become a heavily-cited man and gets most of the credits, even if fluent APIs were there before. One of Java’s oldest “fluent APIs” can be seen in java.lang.StringBuffer, which allows for appending arbitrary objects to a String. But the biggest benefit of a fluent API is its ability to easily map “external DSLs” into Java and implement them as “internal DSLs” of arbitrary complexity.

 

From http://lukaseder.wordpress.com/2012/01/05/the-java-fluent-api-designer-crash-course/

Published at DZone with permission of Lukas Eder, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Witold Szczerba replied on Mon, 2012/01/16 - 7:13pm

Thank you Lukas for nice introduction to creating DSL API.

You are providing examples of JOOQ, I would like to mention the similar project I have discovered recently: QueryDSL. They have a number of modules, covering: JPA, JDO, SQL, Java Collections, RDF, Lucene, Hibernate Search and MongoDB.

Let me show you a very basic example of QueryDSL-JPA, which is much much better than JPA Criteria API:

    private Issue getIssue(Long issueId) {
        QIssue issue = QIssue.issue;
        return new JPAQuery(em) 
                .from(issue) 
                .where(issue.id.eq(issueId)) 
                .singleResult(issue);
    }

In the example above, there is one JPA Entity: Issue and one auto-generated type: QIssue, used for type-safe, LINQ-like queries. The only difference is that for LINQ to work, Microsoft had to enhance the language which is not the case here.

Regards,
Witold Szczerba

Lukas Eder replied on Mon, 2012/05/21 - 6:44am in response to: Witold Szczerba

Witold, QueryDSL surely seems similar at first sight. But when you have a closer look at its "DSL", it turns out to be an enumeration of "builder" or "fluent API" methods, as Martin Fowler mentions. In fact, it is perfectly OK in QueryDSL to write things like:

private Issue getIssue(Long issueId) {
    QIssue issue = QIssue.issue;
    return new JPAQuery(em) 
            .where(issue.id.eq(issueId)) 
            .from(issue) 
            .where(issue.id.eq(issueId)) 
            .from(issue) 
            .singleResult(issue);
}

This is not possible with jOOQ, as jOOQ models a BNF notation of SQL using interfaces, the way I depicted them. See also a more in-depth comparison between the tools here:

http://blog.jooq.org/2012/05/17/onewebsql-another-competitor-in-the-sql-schema-generation-business/

Heiner Kücker replied on Fri, 2014/01/31 - 5:47am

Hey Lukas,

there is an code generator for fluent interface in Java based on grammar.

http://heinerkuecker.de/Fluent_Interface_Code_Generator_auf_Basis_einer_Grammatik.html

Sorry, the Documentation is only in German, but Class Names and Variable Names in example code are english.

The examples are very simple to understand.

The downloadable zip file contains a eclipse project to import in your eclipse workspace.


Regards

Heiner




Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.