Builder Pattern Deluxe
July 12, 2007
Update: code available at http://code.google.com/p/garbagecollected/
Yesterday evening I came up with an interesting approach for implementing Josh Bloch’s revised GoF Builder pattern (warning: PDF). After some late night hacking, I can’t help but feel that this is very useful stuff. Take a look at Josh’s presentation first, and then take a look at this:
package builder;
public class SomeObject {
private final String mandatory;
private final int optional1;
private final char optional2;
private SomeObject (SomeObjectBuilder builder, String mandatory) {
this.mandatory = mandatory;
this.optional1 = builder.optional1();
this.optional2 = builder.optional2();
}
public interface SomeObjectBuilder extends Builder {
SomeObjectBuilder optional1(int optional1);
SomeObjectBuilder optional2(char optional2);
int optional1();
char optional2();
}
public static SomeObjectBuilder builder (final String mandatory) {
return BuilderFactory.make (SomeObjectBuilder.class,
new BuilderCallback () {
public SomeObject call (SomeObjectBuilder builder) throws Exception {
return new SomeObject(builder, mandatory);
}
});
}
public String toString() {
return new StringBuilder()
.append (getClass().getName())
.append (String.format ("[optional1=%s, ", optional1))
.append (String.format ("optional2=%s, ", optional2))
.append (String.format ("mandatory=%s]", mandatory)).toString();
}
public static void main(String[] args) {
System.out.println(SomeObject.builder("Mandatory!")
.optional1(35)
.optional2('A')
.build()
.toString()
);
}
}
Console output: SomeObject[optional1=35, optional2=A, mandatory=Mandatory!]
Using a dynamic proxy, the BuilderFactory provides the Builder<T> implementation for a given interface, so that you don’t have to write all that horrible boilerplate code. Often you use a builder when constructors get messy, but Builders with many parameters get messy too. Using this approach you not only save time, you also have the advantage of using a static factory method and having your specific builder as an interface instead of a concrete class. Full source code available upon request; feedback/suggestions/improvements appreciated!
Eat that, setter injection ;-)
October 30, 2007 at 1:18 am
Came across your website. I happened to be looking at the Builder pattern and I would like to see your source code, especially for BuilderFactory and BuilderCallback.
December 2, 2007 at 11:16 pm
See http://code.google.com/p/garbagecollected/ for source code.
June 4, 2009 at 12:40 am
This implementation won’t with constructors that take an interface as a constructorArg, right? Since you use constructor.getClass() in the BuilderFactory, you’ll get the runtime class. When constructor.newInstance() is called, the types won’t match.
Example:
private SomeClass(SomeClassBuilder, List list)
// caller code
SomeClass.builder(new ArrayList ()).build()
The BuilderFactory will newInstance() with a constructor of types(SomeClassBuilder, ArrayList) and a MethodNotFoundException will be thrown.
June 4, 2009 at 12:41 am
This implementation won’t *work* with constructors… Sorry for the typo =).