Chapter 32. Expression Builder

An object, or family of objects, that provides a fluent interface over a normal command-query API.

image

APIs are usually designed to provide a set of self-standing methods on objects. Ideally, these methods can be understood individually. I call this style of API a command-query API; it’s so normal that we don’t have a general name for it. DSLs require a different kind of API, what I call a fluent interface, which is designed with the goal of readability of a whole expression. Fluent interfaces lead to methods that make little sense individually, and often violate the rules for good command-query APIs.

An Expression Builder provides a fluent interface as a separate layer on top of a regular API. This way you have both styles of interface and the fluent interface is clearly isolated, making it easier to follow.

32.1 How It Works

An Expression Builder is an object that provides a fluent interface which it then translates into calls on an underlying command-query API. You can think of it as a translation layer that translates the fluent interface into the command-query API. An Expression Builder is often a Composite [GoF] using child Expression Builders to build subexpressions within an overall clause.

Exactly how you arrange Expression Builders depends very much of the kind of clause you are dealing with. Method Chaining is a sequence of method calls, each returning an Expression Builder; Nested Function may use an Expression Builder that is a superclass or a set of global functions. As a result, I can’t really give any general rules for what an Expression Builder looks like in this pattern—you need to look at the different kinds of Expression Builders shown in the other internal DSL patterns. What I can do is talk a little about some general guidelines that I think will help you put together a clear layer of Expression Builders.

One of the most notable questions is whether to have a single Expression Builder object for the whole DSL, or whether to use multiple Expression Builders for different parts of the DSL. Multiple Expression Builders usually follow a tree structure that really is a syntax tree for the DSL. The more complex the DSL, the more valuable a tree of Expression Builders is.

One of the most useful tips for getting a clearly separated set of Expression Builders is to ensure you have a well-defined Semantic Model. The Semantic Model should have objects with command-query interfaces that can be manipulated without any fluent constructs. You can verify this by being able to write tests for the Semantic Model that don’t use any DSLs. It may not be wise to force this rule too much; after all, the whole point of an internal DSL is to make it easier to work with these objects, so usually it will be easier to manipulate them in tests with the DSL than with the command-query interface. But I’d usually include at least some tests that only use the command-query interface.

Expression Builders then can act on top of these model objects. You should be able to test the Expression Builders by comparing the Semantic Model objects they manipulate, using direct calls to the Semantic Model command-query APIs.

32.2 When to Use It

I consider Expression Builder a default pattern—meaning I tend to use it pretty much all the time unless there’s a good reason not to.

This, of course, begs the question of when are there occasions when Expression Builder isn’t a good idea?

The alternative to using an Expression Builder is to put the fluent methods on the Semantic Model itself. The main reason I dislike this is that it intermingles the API for building the Semantic Model with the methods that run the model. Usually, each of these two aspects is quite involved. The Semantic Model execution logic often requires an effort to understand, particularly if it represents an alternative computational model. Fluent interfaces have their own logic to maintain flow. So my argument for an Expression Builder boils down to a separation of concerns. It’s easier to understand if we separate building logic from execution logic.

A further reason to separate is that a fluent interface is unusual. Mixing both fluent and command-query methods on the same class intermingles two different ways of representing an API. The fact that fluent APIs are rarer, and thus developers are less familiar with them, exacerbates the situation.

The best argument I see for not using Expression Builder is when the execution logic on the Semantic Model is pretty simple, so that mixing it into the building logic doesn’t really add any complexity.

It is, however, pretty frequent to combine the two. This occurs partly because some people aren’t aware of Expression Builder, and partly because people don’t feel that the additional classes for an Expression Builder are worthwhile. I prefer lots of little classes to a few big classes, so my fundamental design philosophy encourages me to use Expression Builder.

32.3 A Fluent Calendar with and without a Builder (Java)

To explore how an Expression Builder works, I’ll explore building up an event calendar with and without a builder. Essentially, I want to add events to a calendar with a DSL like this:

image

To do this, I’m creating fluent interfaces for the calendar and event classes.

image

(The built-in date and time classes in Java are worse than awful, so I’m using JodaTime here, which is very usable.)

This is a nice interface for building up these things, but the style of interface is different to what most people expect for an object. The methods look somewhat odd next to methods like getStartTime() or contains(LocalDateTime). This is particularly so if you want people to be able to change an event outside the DSL context. In this case, you’ll need to provide regular command-query mutators, such as setStartTime, as well. (Using a fluent interface outside its context would lead to hard-to-read code.)

The basic idea of an Expression Builder is to move these fluent methods onto a separate builder class that uses regular command-query methods on the domain classes.

image

This makes using the DSL a little different.

image

32.4 Using Multiple Builders for the Calendar (Java)

Here’s an absurdly simple version of using multiple builders with the same Calendar example. To motivate this, let’s assume that an event is immutable and all its data has to be created in a constructor. It’s a stretch, but it saves me having to make up another example.

With this, I need to capture the data for an event as I build up the fluent expression. I could do this with fields in the calendar builder (e.g., currentEventStartTime) but it seems better to make an event builder to do this (essentially, using a Construction Builder).

The DSL script is just the same as with a single object builder.

image

The calendar builder is different in that it stores a list of event builders, and add returns an event builder.

image

The event builder captures the data about an event in its own fields using the fluent interface.

image

The add method indicates punctuation for the next event. Since the event builder will receive this call, it needs a method for it, which it delegates to its parent to build the new event builder.

image

When the builder is asked for its content, it creates the whole structure of Semantic Model objects.

image

In Java, a variation on this scheme is to make the child builder an inner class of the parent. With this approach, you don’t need the parent field. (For examples in this book I’ve not done this, as I feel this gets a bit too far into Java’s idiosyncrasies for a multilanguage book.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.221.133