To date I use the following implementation of the builder pattern (as opposed to the implementation described here):
public class Widget { public static class Builder { public Builder(String name, double price) { ... } public Widget build() { ... } public Builder manufacturer(String value) { ... } public Builder serialNumber(String value) { ... } public Builder model(String value) { ... } } private Widget(Builder builder) { ... } }
This works well for most situations I’ve encountered where I need to build up a complex object with a variety of required/mandatory and optional parameters. However, I’ve been struggling lately to understand how the pattern is of any benefit when all your parameters are mandatory (or at least the vast majority are).
One means of getting around this has been to logically group the parameters being passed in to their own classes to reduce the number of parameters being passed to the builder constructor.
For example, instead of:
Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8) .addOptional(opt9) .build();
becomes grouped as follows:
Object1 group1 = new Object1(req1, req2, req3, req4); Object2 group2 = new Object2(req5, req6); Widget example2 = new Widget.Builder(group1, group2, req7, req8) .addOptional(opt9) .build();
While having separate objects simplifies things quite a bit, it also makes things a little difficult to follow if one is not familiar with the code. One thing I considered was moving all parameters into their own addParam(param)
methods and then performing validation on required parameters in the build()
method.
What is best practice and is there perhaps a better approach to this that I haven’t considered?
Advertisement
Answer
However, I’ve been struggling lately to understand how the pattern is of any benefit when all your parameters are mandatory (or at least the vast majority are).
The fluent builder pattern is still beneficial:
Its more readable – it effectively allows named parameters so that the call isn’t just a long list of unnamed arguments
Its unordered – this lets you group arguments together into logical groups, either as part of a single builder setter call or simply by letting you use a natural order to calling the builder setter methods that make the most sense of this particular instantiation.
Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8) .addOptional(opt9) .build();becomes grouped as follows:
Object1 group1 = new Object1(req1, req2, req3, req4); Object2 group2 = new Object2(req5, req6); Widget example2 = new Widget.Builder(group1, group2, req7, req8) .addOptional(opt9) .build();While having separate objects simplifies things quite a bit, it also makes things a little difficult to follow if one is not familiar with the code. One thing I considered was moving all parameters into their own
addParam(param)
methods and then performing validation on required parameters in thebuild()
method.
I would favor a hybrid when appropriate or natural. It doesn’t have to be all in constructor or each param has its own addParam method. Builder gives you flexibility to do one, the other, in-between, or a combo:
Widget.Builder builder = new Widget.Builder(Widget.BUTTON); builder.withWidgetBackingService(url, resource, id); builder.withWidgetStyle(bgColor, lineWidth, fontStyle); builder.withMouseover("Not required"); Widget example = builder.build();