Evolutionary architecture and emergent design: Fluent interfaces

Build internal DSLs to capture idiomatic domain patterns

This installment of Evolutionary architecture and emergent design continues the discussion of harvesting techniques for idiomatic patterns in emergent design. When you identify a reusable pattern, you should capture it in a way to sets it apart from the rest of your code. Domain-specific languages (DSLs) offer many techniques for concisely capturing data and functionality. This month, Neal Ford shows you three ways to build internal DSLs that capture idiomatic domain patterns.

Neal Ford, Software Architect / Meme Wrangler, ThoughtWorks Inc.

Photo of Neal FordNeal Ford is a software architect and Meme Wrangler at ThoughtWorks, a global IT consultancy. He also designs and develops applications, instructional materials, magazine articles, courseware, and video/DVD presentations, and he is the author or editor of books spanning a variety of technologies, including the most recent The Productive Programmer. He focuses on designing and building large-scale enterprise applications. He is also an internationally acclaimed speaker at developer conferences worldwide. Check out his Web site.



13 July 2010

Also available in Chinese Japanese Portuguese

The preceding installment of this series introduced the subject of using domain-specific languages (DSLs) to capture domain idiomatic patterns. This installment continues with that topic, demonstrating various DSL construction techniques.

In his upcoming book Domain Specific Languages, Martin Fowler differentiates between two types of DSLs (see Resources). External DSLs build a new language grammar, requiring tools like lexx and yacc or Antlr. An internal DSL builds new languages atop a base language, whose syntax it borrows and stylizes. The examples in this installment build internal DSLs using Java™ as the base language, constructing new mini-languages on top of its syntax.

About this series

This series aims to provide a fresh perspective on the often-discussed but elusive concepts of software architecture and design. Through concrete examples, Neal Ford gives you a solid grounding in the agile practices of evolutionary architecture and emergent design. By deferring important architectural and design decisions until the last responsible moment, you can prevent unnecessary complexity from undermining your software projects.

Underlying all the following techniques for constructing DSLs is the notion of implicit context. DSLs (especially internal ones) try to eliminate noisy syntax by creating contextual wrappers around related elements. A good example of this concept appears in XML in the form of parent and child elements, which provide a wrapper around related items. You'll notice many of these DSL techniques achieve that same effect using language syntactic tricks.

Readability is one of the benefits of using a DSL. If you write code that nondevelopers can read, you shorten the feedback loop between your team and the people who are requesting features. A common DSL pattern identified in Fowler's book is called fluent interface, which he defines as behavior capable of relaying or maintaining the instruction context for a series of method calls. I'll show you several types of fluent interfaces, starting with method chaining.

Method chaining

Method chaining uses return values from methods to relay instruction context, which in this case is the object instance making the first method invocation. This sounds much more complex than it is, so I'll show an example to clarify this concept.

When working with DSLs, it is common to start with your goal syntax and reverse engineer backwards to figure out how to implement it. Starting at the end makes sense because readability is highly valued in DSLs. The example I'll use is a small application that tracks calendar entries. The application illustrates the DSL's syntax, as shown in Listing 1:

Listing 1. Goal syntax for a calendar DSL
public class CalendarDemoChained {

    public static void main(String[] args) {
        new CalendarDemoChained();
    }

    public CalendarDemoChained() {
        Calendar fourPM = Calendar.getInstance();
        fourPM.set(Calendar.HOUR_OF_DAY, 16);
        Calendar fivePM = Calendar.getInstance();
        fivePM.set(Calendar.HOUR_OF_DAY, 17);

        AppointmentCalendarChained calendar =
                new AppointmentCalendarChained();
        calendar.add("dentist").
                from(fourPM).
                to(fivePM).
                at("123 main street");

        calendar.add("birthday party").at(fourPM);
        displayAppointments(calendar);
    } 

    private void displayAppointments(AppointmentCalendarChained calendar) {
        for (Appointment a : calendar.getAppointments())
            System.out.println(a.toString());
    }
}

After the necessary cruft at the top dealing with Java calendars, you can see the method-chaining fluent interface in action as I add values to the two calendar entries. Notice that I'm using white space to separate the parts of what is (from a Java syntax standpoint) a single line of code. It is common in internal DSLs to stylize the use of the base language to make the DSL more readable.

The Appointment class containing most of the fluent-interface methods appears in Listing 2:

Listing 2. Appointment class
public class Appointment {
    private String _name;
    private String _location;
    private Calendar _startTime;
    private Calendar _endTime;

    public Appointment(String name) {
        this._name = name;
    }

    public Appointment() {
    }

    public Appointment name(String name) {
        _name = name;
        return this;
    }
    public Appointment at(String location) {
        _location = location;
        return this;
    }

    public Appointment at(Calendar startTime) {
        _startTime = startTime;
        return this;
    }

    public Appointment from(Calendar startTime) {
        _startTime = startTime;
        return this;
    }

    public Appointment to(Calendar endTime) {
        _endTime = endTime;
        return this;
    }

    public String toString() {
        return "Appointment:"+ _name +
                ((_location != null && _location.length() > 0) ? 
                    ", location:" + _location : "") +
                ", Start time:" + _startTime.get(Calendar.HOUR_OF_DAY) +
                (_endTime != null? ", End time: " + 
                _endTime.get(Calendar.HOUR_OF_DAY) : "");
    }
}

As you can see, building fluent interfaces is straightforward. For each of the mutator methods, you diverge from standard JavaBean syntax by writing your setter methods to return the host object (this) and by replacing the set naming convention with something more readable. The general definition at the start of this section should now be clear. The context being relayed via the method chaining is this, meaning that you can make a series of method calls concisely.

In the article "Leveraging reusable code, Part 2," I showed an API definition for a train car, as shown in Listing 3:

Listing 3. An API for a train car
Car2 car = new CarImpl();
MarketingDescription desc = new MarketingDescriptionImpl();
desc.setType("Box");
desc.setSubType("Insulated");
desc.setAttribute("length", "50.5");
desc.setAttribute("ladder", "yes");
desc.setAttribute("lining type", "cork");
car.setDescription(desc);

The problem domain for train cars is complex because of regulatory rules about contents and history. On the project that yielded this example, we had lots of complicated testing scenarios that required dozens of lines of set calls like the ones in Listing 3. We tried to get our business analysts to verify that we had the right magical combination of attributes, but they pushed back because they viewed it as Java code, which they had no interest in reading. The ultimate impact of this problem was the requirement that a developer verbally translate the details, which is of course error-prone and time-consuming.

To resolve this problem, we converted our Car class into a fluent interface, so that the code from Listing 3 became the fluent interface shown in Listing 4:

Listing 4. Fluent interface for train cars
Car car = Car.describedAs()
             .box()
             .length(50.5)
             .type(Type.INSULATED)
             .includes(Equipment.LADDER)
             .lining(Lining.CORK);

This code was declarative enough and removed sufficient noise from the Java API version that our business analysts were happy to verify it for us.

Returning to the calendar example, the last bit of the implementation is the AppointmentCalendar class, which appears in Listing 5:

Listing 5. AppointmentCalendar
public class AppointmentCalendarChained {
    private List<Appointment> appointments;

    public AppointmentCalendarChained() {
        appointments = new ArrayList<Appointment>();
    }

    public List<Appointment> getAppointments() {
        return appointments;
    }

    public Appointment add(String name) {
        Appointment appt = new Appointment(name);
        appointments.add(appt);

        return appt;                        
    }
}

The add() method:

  1. Starts the method chain by creating a new Appointment instance
  2. Adds the new instance to the list of appointments
  3. Ultimately returns the new appointment instance, meaning that subsequent method calls are invoked on the new appointment

When you run the application, you see the details of your configured appointments, as shown in Figure 1:

Figure 1. Results of running the calendar application
output from demo application

So far, method chaining looks like a simple way to clean up overly verbose syntax, especially method calls that are mostly declarative. This works well for idiomatic patterns in emergent design because domain patterns are frequently declarative.

Note that using method chaining necessitates violating the syntactic rules for JavaBeans, which insist that mutator methods must start with set and return void. Building fluent interfaces is an example of knowing when it makes sense to break some of the rules. The JavaBeans specification isn't doing you any favors if it forces you to write obfuscated code! But nothing in the creation or use of fluent interfaces precludes supporting both the fluent interface and a JavaBeans interface. The fluent interface methods can turn around and call the standard set methods, allowing you to use fluent interfaces even when frameworks insist that it interact with your classes as JavaBeans.


Solving the finishing problem

One pitfall that's inherent in fluent interfaces under certain circumstances is known as the finishing problem. I'll illustrate this issue by making a change to the AppointmentCalendar class from Listing 5. Presumably, you'd like to do more than just display the appointments, such as put them in a database or some other persistence mechanism. Where do you add the code to save the completed appointment to storage? You can try to do it in AppointmentCalendar's add() method just before you return the appointment. Listing 6 shows an attempt to access the appointment here for something as simple as printing it:

Listing 6. Adding printing
public Appointment add(String name) {
    Appointment appt = new Appointment(name);
    appointments.add(appt);
    System.out.println(appt.toString());
    return appt;
}

When you run the code in Listing 6, the unhappy results illustrated in Figure 2 show up:

Figure 2. Error output after the addition to AppointmentCalendar
Output from changes to AppointmentCalendar

The error displayed is a NullPointerException occurring in the toString() method on the Appointment class. Why it complains here, even though the method worked correctly, is the essence of the finishing problem.

The error occurs because I'm trying to call the toString() method on the appointment instance before the rest of the fluent-interface setter methods are called. The code to try printing the appointment appears in the method that creates the appointment instance and starts the chain. I could create a save() or finished() method that must be called as the last method in the chain, but I'd rather not impose an easy-to-forget rule on my DSL's users. In fact, I'd rather not impose any order semantics on the methods in my fluent interface.

The real problem is that I'm being too aggressive with the method-chaining technique. Method chaining works best for the creation of simple data objects, yet here I'm using it both for the setter methods on Appointment and in AppointmentCalendar to start the method chain.

I can fix the finishing problem by wrapping the creation of the appointment entirely with the parentheses of the appointment calendar's add() method, as shown Listing 7:

Listing 7. Wrapping via parameter
AppointmentCalendar calendar = new AppointmentCalendar();
calendar.add(
        new Appointment("Dentist").
        at(fourPM));
calendar.add(
        new Appointment("Conference Call").
        from(fourPM).
        to(fivePM).
        at("555-123-4321"));
calendar.add(
            new Appointment("birthday party").
            from(fourPM).
            to(fivePM)).
        add(
            new Appointment("Doctor").
            at("123 Main St"));
calendar.add(
        new Appointment("No Fluff, Just Stuff").
        at(fourPM));
displayAppointments(calendar);

In Listing 7, the add() method's parentheses encapsulate the entire use of the Appointment fluent interface, allowing the add() method to handle whatever additional behavior that it wants (printing, persistence, and so on). In fact, I couldn't resist adding a bit of a fluent interface to AppointmentCalendar itself: you can now chain together the add() methods, as shown in Listing 7 and implemented in Listing 8:

Listing 8. The parameter-wrapping AppointmentCalendar
public class AppointmentCalendar {
    private List<Appointment> appointments;

    public AppointmentCalendar() {
        appointments = new ArrayList<Appointment>();
    }

    public AppointmentCalendar add(Appointment appt) {
        appointments.add(appt);
        return this;
    }

    public List<Appointment> getAppointments() {
        return appointments;
    }
}

The finishing problem can arise any time you mix fluent-interface classes. It popped up in this example because I used the appointment calendar to start the method chain, mixing the construction and wrapping behaviors. By deferring construction and initialization to the Appointment class, I make it easier to separate additional wrapping behavior (such as persistence).


Wrapping via functional sequence

Thus far, I've shown two of the three context-passing techniques for fluent-interface DSLs. The third — functional sequence — uses inheritance and anonymous inner classes to create a context wrapper. The calendar application rewritten using functional sequence appears in Listing 9:

Listing 9. Wrapping via functional sequence
calendar.add(new Appointment() {{
    name("dentist");
    from(fourPM);
    to(fivePM);
    at("123 main street");
}});

calendar.add(new Appointment() {{
    name("birthday party");
    at(fourPM);
}});

Listing 9 shows a pattern that I introduced in "Leveraging reusable code, Part 2" under the guise of removing structural duplication. The syntax looks odd because of the double {{ braces. The first set of enclosing braces delineates the construction of an anonymous inner class, and the second set delineates the instance initializer for the anonymous inner class. (If this sounds a bit confusing, you can refer back to "Leveraging Reusable Code, Part 2" for a lengthy explanation of this Java idiom.)

The main advantage of this style of fluent interface lies in its adaptability. The only thing a class needs to be used this way is a default constructor (which allows you to create an anonymous inner class instance inheriting from your class). That means that you can easily add fluent-interface methods to existing Java APIs without changing any of the current calling semantics. This allows you to "fluentize" existing APIs gradually.


Conclusion

DSLs capture idiomatic domain patterns concisely and effectively. Fluent interfaces provide a simple way to change the way you write code so that you can more readily see the idiomatic patterns you've fought to identify. They also force you to change your perspective on code just a bit: it should be not merely functional but readable as well, especially if nondevelopers need to consume any aspect of it. Fluent interfaces remove unnecessary noise from your syntax, allowing for more readable code. For declarative structures, you can express ideas more clearly with less effort.

In the next installment, I'll continue discussing DSL techniques as a mechanism for harvesting idiomatic patterns in emergent design.

Resources

Learn

Get products and technologies

  • Antlr: Antlr is a powerful language design tool, giving you the ability to create new parsers and lexers for custom languages.
  • Evaluate IBM® products in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement Service Oriented Architecture efficiently.

Discuss

  • Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=500146
ArticleTitle=Evolutionary architecture and emergent design: Fluent interfaces
publish-date=07132010