This new and unorthodox style has been espoused by such giants as Martin Fowler, such average-heighted people as Steve Asher, and such midgets as Mike Finney. Knowing my place in the order of things, I suppose it is my turn. I'll start out by reiterating some of points being made.
- Fluent interfaces are a rockin' way to approximate the flexibility of domain specific languages (DSLs) using OO constructs.
- Fluent interfaces are easy to use, but relatively difficult to design.
- Fluent interfaces violate several "best practices" of interface design (such as command query separation), but are often a Good Thing nevertheless.
- If Sun finally gives us closures in Java 7, the skys will part, the rapture will be upon us, and we will all join hands singing "Camptown Races". Also, fluent interfaces will become cooler.
What qualities does a grammar need to have to create an embedded DSL which doesn’t allow nonsensical constructs?
Before addressing this problem, let us set some ground rules. First, we live in Java 5. Second, it is not sufficient for our interface to accept some superset of the desired language; our grammar must be an exact fit. Ungrammatical statements must not compile. They should also be underlined by our spiffy IDE :)
Note: In formal language theory, a grammar is a set of transformation rules and a language is the set of all strings that the grammar accepts. Some background with these concepts is assumed.
Fluent interfaces generally use two ways of combining tokens to form statements: Call-chaining as().seen().here(), and nesting which(works(like(so))). Some interfaces stick to one or the other, while Steve's customer creator uses both:
Customer customer =He explains,
The first thing to notice is that almost half of this code is fully enclosed in parenthesis. The result of 'bought.item(CompactDisc).on("02/17/2006")' is being passed to a method named 'that'.
By contrast, this is what that statement would look like using call-chaining all the way:
Customer customer =And here is one of the possible translations using only nesting:
Customer customer =
The customer creator's grammar would look similar to this.
S ::= createCustomer CExamples of strings accepted by this grammar:
C ::= $
C ::= namedSomething C
C ::= that T C
C ::= and T C
T ::= bought someItem
T ::= bought someItem onSomeDate
createCustomerSo far I have introduced the idea of formal grammars for fluent interfaces, and posed a question about their limitations. In future posts I will answer this question for each variant of fluent interfaces: call-chaining, nesting, and mixed. Have faith! I am going somewhere with this.
createCustomer namedSomething that bought someItem
createCustomer that bought someItem and bought someItem onSomeDate
P.S. Steve and Mike: I was just kidding, you are both well above average height.