(This post is a bit "off the Rails"... I do still think about Java every now and then, like an old girlfriend... a bloated corporate girlfriend who talks a lot without saying much.)
I've been a fan of the Builder pattern for a while, and appreciate Martin Fowler's article on fluent interfaces as well as a variety of real-world examples. Combining the two is pretty sexy, especially in a relatively noisy language like Java -- I like to call the combination a "fluent builder". In many cases, a fluent builder provides a form of named parameters, making code that uses the API very easy to read and understand. Fluent builders are useful in constructing immutable objects (love 'em!) without requiring overloaded/telescoping constructors with lots of arguments, particularly when some or all of the arguments are of the same type (an obvious use case for named parameters).
I seem to be in good company. I came across a presentation by Josh Bloch that discusses this as part of his work on a long overdue version of his outstanding book Effective Java. I'd really like to see some of his examples make their way into the JDK, like the HashMap factory.
As discussed in Bloch's presentation and elsewhere, static factory methods are a simple alternative to constructors, but are much more powerful. You can't have more than one constructor with the same set of argument types, but you can have multiple factory methods. You can't return a cached/singleton/pooled instance from a constructor, but you can from a factory method. You can't return an instance of a sub-type from a constructor, but you can from a factory method. Each static factory method can have a meaningful name that contributes to readable, fluent code.
The point is that static factory methods are an important part of building a flexible fluent interface. You don't always need them, but the cost of writing them is so low, and the potential benefit is so great (even if YAGNI), you might as well.
Here's a contrived example of some code that doesn't use a fluent builder (yet):
Car car = new Car();
car.setYear(2007);
car.setMake("Toyota");
car.setModel("Camry");
car.setColor("blue");
This is perfectly fine, but we can do better. A car's year, make, and model are certainly not going to change, and for our purposes we'll assume that it will never be repainted. Let's pass these args to the constructor instead of using setters, which allows us to make these attributes immutable.
Car car = new Car(2007, "Toyota", "Camry", "blue");
Much more concise, but it's not going to be easy to remember the order of all of those arguments, especially since there are three Strings. Let's try it now with a fluent builder:
Car car = Car.builder()
.year(2007)
.make("Toyota")
.model("Camry")
.color("blue")
.build();
Let's examine what this code is doing. First, we call the static factory Car.builder() to get our fluent builder object. Then we call methods on the builder object to initialize the year, make, model, and color. It's very clear which fields have which values. The methods are chained together, which isn't necessary (we could cache the builder in a local variable) but is usually preferable because it reduces noise. Finally, we call the builder's build() method, which creates the Car object with the attributes we've given the builder.
We could put all of the chained method calls (year, make, model, color) on a single line, but it's usually more readable to put each on a separate line. It may be useful to note that we have some leeway when it comes to the order, as long as we finish the chain with a call to the build method (which returns the immutable Car instance).
# It doesn't matter what order we set the values in.
Car car = Car.builder()
.color("blue")
.make("Toyota")
.model("Camry")
.year(2007)
.build();
Here's the code for the Car and its Builder:
public class Car {
public static class Builder {
private int year;
private String make;
private String model;
private String color;
public Builder year(int year) { this.year = year; return this; }
public Builder make(String make) { this.make = make; return this; }
public Builder model(String model) { this.model = model; return this; }
public Builder color(String color) { this.color = color; return this; }
public Car build() {
return new Car(year, make, model, color);
}
}
private final int year;
private final String make;
private final String model;
private final String color;
private Car(int year, String make, String model, String color) {
this.year = year;
this.make = make;
this.model = model;
this.color = color;
}
public static Builder builder() {
return new Builder();
}
}
There is some repetition. The Builder must duplicate all of the fields that it needs to build the Car. There's some potential refactoring, particularly to abstract a simple interface and some generic and reusable behavior, maybe backing it with a HashMap.
The code is much more complex than just using setters or a constructor. The benefits may not be clear right away, but I think that using the fluent builder provides a much more pleasant, programmer-friendly API in the long run.

4 comments:
I'm still getting used to fluent interfaces. I do like the IDE support they can provide, but I'm wondering if this is the right kind of use case for them.
It seems to me that if you're usng a language that supports named arguments that the difference between:
Car = car.Builder()
.year(2007)
.make("Toyota")
.model("Camry").build()
and
car = car.build(year='2007', make='Toyota', model='Camry');
is (a) very small and (to my mind) (b) the second syntax is tighter while being equally readable, and of course requiring no special programming to implement.
Ahh, OK, my bad. Java doesn't support named arguments - the things you take for granted! I guess most Java coders would just pass in a value object encapsulating the properties, but then I could see how a fluent builder would be more readable.
You've gotta love accidental complexity in programming languages!
Hi Peter, thanks for the comment! Yes, when you have named parameters or something similar in the language fluent builders lose some of their luster. In Ruby you can use a hash, and a common idiom in Rails is to pass a hash to the constructor or another method:
car = Car.new :year => 2007, :make => 'Toyota', :model => 'Camry', :color => 'blue'
Many of the GoF Design Patterns were born out of limitations in C++, have survived in Java because of similar limitations, but haven't caught on in languages like Python and Ruby because of language features like dynamic/duck typing and meta-programming.
hello. i've found this article really interesting!
can you suggest me any blog similar to yours but focused on java language instead of ruby?
thanks in advance
paolo
Ohhh, those screenshots are reminiscent of my computer study days on terminals and playing text adventure games! :=)
Post a Comment