Structured Fluent Interfaces

The last post showed how a fluent interface can be used to construct structured data. If data is seen as a tree, it can be described entirely in depth first fashion, some method calls descending in a branch, and some others going back to the parent node. When reading the code, however, it may become non-obvious which “descending” method call corresponds to a given “returning” method call.

For instance, in the above code, line 26 matches line 23.

Level Seven, Manually Indent Method Calls:

base
    .from(morning)
      .from(monday)
      .to(friday)
    .to(evening)
    .print();

but incorrect indentation will be impossible to detect will not cause warnings or errors at compilation or execution time, and automatic formatting tools will destroy it, hence the Level Seven grade. When constructing complex structures we need to find a way to have actual syntactic elements reflecting that structure.

Level Three, Commented Brackets:

If your IDE highlights matching brackets in comments you may obtain a Level Three solution to finding matching method calls:

base
    .from(morning) // (
      .from(monday) // (
      .to(friday) // )
    .to(evening) // )
    .print();

Pointing to the commented bracket on line five should highlight the bracket on line 2. I am calling this Level Three as it should be visually obvious if a from or to has no, or a wrong, commented bracket next to it.

Level One, Substructure as Method Parameter:

base.range(
      from(morning).range(
        from(monday)
        .to(friday))
      .to(evening))
    .print();

Make sure that the range() method takes an object that only to() returns. In this code, pointing to the last closing bracket on line five highlights the corresponding opening bracket on line one. Furthermore, a properly configured formatter should preserve the indentation: lines 2-5 have extra indentation due to being part of the range() method call, and lines 3-4 have extra extra indentation due to being part of the nested range() method call.

Note that, now that we nest method calls, we no longer need nested types in the implementation:

public class To { /* ... */ }
public class From<T extends Comparable<T>> {
    T from;
    // insert constructor etc here
    public From<T> range(To subRange) { /* ... */ }
    public To to(T end) {
        return new To(...);
    }
}
public static <T extends Comparable<T>> From<T> from(T start) {
    return new From<>(start);
}
public SentenceBuilder range(To range) {
    // record data from given object...
    return this;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">