Thursday, October 18, 2012

Implementing a DSL in Java Using the Builder Pattern

DSLs are really just expressive APIs. There are a variety of ways to implement DSLs. The simplest DSL, although limited in its capabilities, is an internal DSL (i.e. a DSL written in an existing programming language). In Java, we can use a builder pattern to design an internal DSL very easily.

Let's say, for example, we want a DSL that we can use to create customer records. In addition to a customer POJO, creating a customer instance would probably look something like what's shown below.

Customer customerOne = new Customer();

customerOne.setFirstName("John");
customerOne.setLastName("Doe");

DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

try {
    Date dateOfBirth = (Date)(formatter.parse("1971-01-26"));
    customerOne.setDateOfBirth(dateOfBirth);
} catch (ParseException e) {
    System.err.println(e.getMessage());
}


This code doesn't look too bad to a Java developer, but for somebody who isn't overly familiar with Java, this looks cryptic. Not only that, but there is quite a bit of code here that doesn't have anything to do with the customer domain. It's just there because that's what Java requires. We can make this quite a bit better by building a fluent DSL using a builder pattern.

Here's what the code looks like for our builder pattern. Remember, this is the code we developers would write to provide a fluent API for creating customer records. This is the DSL. So, don't be overwhelmed by the amount of code. Our goal here is to provide an easy to use API for creating customer records that hides some of the inherent complexity of the Java code we saw above.

public class Customer {

   static class Builder {

      private String firstName;
      private String lastName;
      private Date dateOfBirth;

      public Builder() {}
     
      public Builder withFirstName(String firstName) {
         this.firstName = firstName;
         return this;
      }

      public Builder withLastName(String lastName) {
         this.lastName = lastName;
         return this;
      }

      public Builder bornOn(String dateOfBirthValue) {
         DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
         try {
            Date dateOfBirth = (Date)(formatter.parse(dateOfBirthValue));
            this.dateOfBirth = dateOfBirth;
         } catch (ParseException e) {
            System.err.println(e.getMessage());
         }

         return this;
      }

      public Customer build() {
         return new Customer(this);
      }
   }

   private final String firstName;
   private final String lastName;
   private final Date dateOfBirth;

   private Customer(Builder b) {
      firstName = b.firstName;
      lastName = b.lastName;
      dateOfBirth = b.dateOfBirth;
   }

   public String toString() {
      return "First Name = " + this.firstName +
             ",Last Name = " + this.lastName +
             ",Date of Birth = " + this.dateOfBirth.toString();
   }

}


Now to create a customer record, we can use the builder pattern we constructed. The result is an expressive API that focuses on the domain (customer records) and hides the innate complexities of Java.


Customer c = new Customer.Builder()
   .withFirstName("John")
   .withLastName("Doe")
   .bornOn("1971-01-26")
   .build();


This type of implementation still comes with some of the nuances of its implementation language. Languages like Groovy and Scala do a better job of creating internal DSLs because of their support for higher-order functions (functions that take other functions as parameters). JDK 8 promises to do that for us and I look forward to being able to take advantage of them. In the meantime, Groovy and Scala are great languages for internal DSL development. I'll cover those examples in a later post. But, there's still alot you can do with internal DSLs in Java 7 and a builder pattern is an easy way to do it.

2 comments:

  1. Hey There. I discovered your blog using msn. This is an extremely well written article.
    I'll be sure to bookmark it and return to read extra of your useful information. Thanks for the post. I will definitely return.

    my web site :: Chip Satış

    ReplyDelete
  2. Your way of telling everything in this piece of writing is actually pleasant,
    all be capable of without difficulty understand it, Thanks a lot.


    Here is my blog article builder discount

    ReplyDelete