Builders
Prediction???
Factories are out; Builders are in.
Basis
Here is some “gyan” laced with content filched from Josh Bloch’s prezo on Builders in Javaone 06/07.
In the Builder pattern:-
• Builder constructor takes all required (manadatory) params
• One setter for each optional parameter
• Setters return the builder to allow for chaining
• One method to generate instance
• Pattern emulates named optional parameters!
Here is an example invocation/usage:-
NutritionFacts locoCola = new NutritionFacts.Builder(240, 8).sodium(30). carbohydrate(28).build();
Here is the builder code for the example above:-
public class NutritionFacts {
public static class Builder {
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val; return this;
}
... // 15 more setters
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
<copy data from Builder to NutritionFacts>
}
}
But writing a builder for each object gets oh so tedious every time. Cant we have something easier (read less code) Mr. Bloch?
Solution
Well, Robbie Vanbrabant has one. See BuilderBuilder.
For any object that you need to create a builder for; this is what you need to do:-
public class Example {
private final String mandatory;
private final int optional1;
private final char optional2;
private Example(ExampleBuilder builder, String mandatory) {
this.mandatory = mandatory;
this.optional1 = builder.getOptional1();
this.optional2 = builder.getOptional2();
}
public interface ExampleBuilder extends Builder {
ExampleBuilder optional1(int optional1);
ExampleBuilder optional2(char optional2);
int getOptional1();
char getOptional2();
}
public static ExampleBuilder builder(final String mandatory) {
return new BuilderFactory(BuilderType.SIMPLE_SETTER)
.makeRisky(ExampleBuilder.class, Example.class, mandatory);
}
public String toString() {
return new StringBuilder()
.append(getClass().getName())
.append(String.format("[optional1=%s, optional2=%s, mandatory=%s]",
optional1, optional2, mandatory)).toString();
}
}
And how do we use it?
Example.builder("Mandatory!").optional1(4).build();
Thats it!
No need to write the Builder code. Just write the interface for the Builder and you are ready to roll!
Well, how does this work?
Dissection
Remember Java Proxies and Reflection. Thats the magic behind it. See the code for BuilderFactory.
Notice how makeRisky() creates a callback handler BuilderCallback and passes it on to make(). Essentially make() creates a Proxy for Class V using an invocationhandler called BuilderInvocationHandler. BuilderInvocationHandler takes in the callback BuilderCallback as an argument.
As is mostly the case with invocationhandlers, the BuilderInvocationHandler creates a map backed storage for all the attributes of Class V, which is accessed whenever a set for an attribute is invoked via invoke(). Note that the set() for an attribute in this pattern always returns the Builder T. In the case where build() is called, it delegates the call to call() method on the callback handler BuilderCallback which returns an object of Class V.
So, essentially the trick works flawlessly with a sprinkling of reflection and callbacks. Cool!
NOTE: Robbie got this done last year. I just happened to stumble over it now!
JEDI Moral: Less YouTube Must Watch This Year