Archive

Posts Tagged ‘java programming’

Strictly Fluent Java

2009/05/10 Leave a comment

Fluent APIs are interfaces that go out of their way to look like a domain language, or even conversational languages, such English. Common uses seem to be factories, and that’s what I’ve used them for most. Implementing these in languages like java will probably leave lots of odd looking methods littered about your classes, like:

  public FluentOrderFactory forCustomer(Customer customer)

because the common pattern for implementing this style of API is via method chaining on a single class:

public class FluentOrderFactory {
  public FluentOrderFactory static placeOrder() { /* snip */ }
  public FluentOrderFactory forCustomer(Customer customer) { /* snip */ }
  public FluentOrderFactory with(int quantity) { /* snip */ }
  public FluentOrderFactory of(Product product) { /* snip */ }
  public FluentOrderFactory and(int quantity) { /* snip */ }
  public FluentOrderFactory deliverTo(Address deliveryAddress) { /* snip */ }
  public FluentOrderFactory andSendinvoiceto(Address invoiceAddress) { /* snip */ }
}

So using static imports(a vital piece of all fluent developers’ toolkit), you can write:

  placeOrder().forCustomer(bob).with(2).of(iPod).with(3).of(dvd).and(4).of(cd).deliverTo(deliveryAddress).andSendinvoiceTo(invoiceAddress);

but equally, you can write:

  with(2).placeOrder().deliverTo(invoiceAddress).of(iPod).andSendinvoiceTo(deliveryAddress).and(3);

which makes no sense what-so-ever. And you wouldn’t know until it ran. Well, you might ;).

But in the client code of this factory, in your favourite IDE, you can easily make these sort of mistakes. It is simple enough to create an API that resembles English when read, but no programming language can have the flexibility of a spoken language. They just have to be so much more specific than the flexible world of human language. It is difficult to make use of pro-nouns for example. And even when you are given the restricted vocabulary of these classes, it is often difficult to guess (or even recall) the intent of some of these methods. You end up having to refer to other clients of the code, the most obvious example of which should be your unit tests. These APIs tend to be read-only ( – the opposite of Perl).

We can do better than this. By separating methods into separate classes, we can enforce a little order to this interface, and remove some of that confusing choice from each stage of the API.

public class FluentOrderFactory {
  public static CustomerStage placeOrder() { /* snip */ }
  public static class CustomerStage {
    public QuantityStage forCustomer(Customer customer) { /* snip */ }
  }
  public static class QuantityStage {
    public ProductStage with(int quantity) { /* snip */ }
    public ProductStage and(int quantity) { /* snip */ }
	public InvoiceStage deliverTo(Address deliveryAddress) { /* snip */ }
  }
  public static class ProductStage {
    public QuantityStage of(Product product) { /* snip */ }
  }
  public static class InvoiceStage {
    public Order andSendinvoiceTo(Address invoiceAddress) { /* snip */ }
  }
}

This limits the mistakes we can make to things like:

  placeOrder().forCustomer(bob).and(3).of(iPod).with(2).of(dvd).deliverTo(deliveryAddress).andSendinvoiceTo(invoiceAddress);

This is a little better. But there are still some holes. We can clean it up a little more if we remove the circular reference, and make that a list by using another Java 1.5 feature varargs.

public class FluentOrderFactory {
  public static CustomerStage placeOrder() { /* snip */ }
  public static class CustomerStage {
    public QuantityStage forCustomer(Customer customer, ProductQuantitiy... productQuantities) { /* snip */ }
  }
  public static class ProductQuantitiy {
    public static ProductQuantitiy with(int quantity, Product product) { /* snip */ }
	public static ProductQuantitiy and(int quantity, Product product) { /* snip */ }
  }
  public static class DeliveryStage {
	public InvoiceStage deliverTo(Address deliveryAddress) { /* snip */ }
  }
  public static class InvoiceStage {
    public Order andSendinvoiceTo(Address invoiceAddress) { /* snip */ }
  }
}

So now we say something like:

  placeOrder().forCustomer(bob, with(3, iPod), and(2, dvd)).deliverTo(deliveryAddress).andSendinvoiceTo(invoiceAddress);

There is something we lose here, and that is the auto-completion IDE hinting for some of the pieces of API. We have to hunt for the static factory method for the ProductQuantitiy class. And we can still mess up the use of “with” and “and”. But that doesn’t sound so likely.

After you’ve built a few of these things, you’ll start to notice a lot of duplicate code. In fact, a lot of the stages will be shared between the more complex objects. To reduce this duplication, we can apply liberal use of another of Java’s 1.5 features, generics, making our little stages look more like:

public abstract class AbstractStage {
  protected T next;
  protected String setter;
  protected AbstractStage(T n) {
    next = n;
  }
  protected T getNextStage() {
      return next;
  }
}

public class DeliveryStage extends AbstractStage {
  public DeliveryStage(T nextStage) {
    super(nextStage);
  }
  public T deliverTo(Address deliveryAddress) { /* snip */ }
}

/* snip */

public class DomainFactory {
  public static CustomerStage<deliveryStage> placeOrder() {
    return new CustomerStage(new DeliveryStage(new InvoiceStage(new Order())));
  }
}

This factory method lets us specify the order of the stages we use for each class that we construct, and share any common methods. It does this at the cost of some readability for the factory, since there’s a little duplication, but if there are many shared phrases in your fluent API, that only differ on return types, then this can be quite an improvement. Also, that chain of generic classes is pretty impenetrable if you’re new to it, but at least the compiler will tell you when you’ve made a mistake.

You can even create a FluentApi class that contains methods like:

  public static  T and(T o) {
    return o;
  }

that can be statically imported to provide a little grammatic sugar.

So really, a fluent API sounds like a nice thing to have in your code-base. And it is. But only in the more complex and prevalent areas of your code, because they add a fair amount of cognitive weight to the creation of objects, and if they are simple then you may well be better off with a “new” keyword. But, if you decide that you need the legibility in your domain, then they are an excellent way of making a start on the path to a domain specific language.

For simpler cases, stick to the separate classes, no generics style. It is easier to manage, and allows your tools to be more helpful. For the more complex cases, generics and reflection can save you from a lot of duplication, and makes you feel clever – which is the most important thing, after all.

Categories: programming Tags:
Design a site like this with WordPress.com
Get started