The builder pattern is just a way to build an object similar to what a constructor does, so why use a builder pattern instead of plain old constructors?
4 Answers
I agree with your view that a Builder is really just a glorified constructor, and that the "builder pattern is just a way to build an object similar to what a constructor does".
However, here are a few of scenarios where the complexity of constructing an object makes the use of a Builder compelling.
Object's dependencies collected over period of time
In Java, StringBuilder is commonly used when building a string over a period of time, or rather, within a complex procedure. For instance, if a server is communicating with a client over a socket, and wants to append some client responses to the string, but not others, and perhaps remove certain responses that were previously appended,the StringBuilder class can be used to do so. At the end of the client/server session, the server can invoke StringBuilder#toString to get the built String.
Lots of parameters
If a constructor has dozens of parameters, it may make the code more readable or easy to maintain to use a builder.
E.g.
new Foo(1,2,3,4,5,6,7,8,9,10,11,12)
Vs.
Foo.newBuilder()
.bar(1)
.bar(2)
.quux(3)
...
.build()
Constructing object graphs
Similar to the "lots of parameters" scenario, I think that the scenario where a builder is most compelling is when constructing a complex object graph. The other answers in this question refer to the telescoping anti-pattern. This scenario (building a complex object graph) can lead to "telescoping", which the Builder helps resolve.
For instance, imagine you have an object-oriented pipeline interface, where a Pipeline depends on Sequence which depends on Stage. A PipelineBuilder would not only provide a nice wrapper around the constructor of Pipeline, but also around the constructors Sequence and Stage, allowing you to compose a complex Pipeline from a single Builder interface.
Instead of telescoping constructors like so:
new Pipeline(
new Sequence(
new Stage(
new StageFunction() {
public function execute() {...}
}
),
new Stage(
new StageFunction() {
public function execute() {...}
}
)
)
)
A PipelineBuilder would allow you to "collapse" the telescope.
Pipeline.newBuilder()
.sequence()
.stage(new StageFunction () {
public function execute() {...}
})
.stage(new StageFunction () {
public function execute() {...}
})
.build()
(Even though I have used indentation in a way that is reflective of the telescoping constructors, this is merely cosmetic, as opposed to structural.)
3 Comments
Object constructed over period of time. This is just wrong. Part of the reason why Builder pattern is preferred over plain old setters is that it ensures that the object is initialized in one go before anyone else can use it. Just like constructors or static factory methods, it ensures that an object will be initialized with the required properties before it is used. Using builder in the fashion you describe in this part of your answer defies the whole point of the Builder pattern!Builder is that "it ensures that the object is initialized in one go before anyone else can use it". What I meant by "constructed over period of time" is really that the dependencies of the constructor can be collected over time within a procedure, and, at the end of the procedure, the object can be constructed by invoking the Builder's build() method, or equivalent. I definitely did not mean to imply that the Builder allows you to instantiate a target class and then modify it over time.From the Wikipedia page:
The telescoping constructor anti-pattern occurs when the increase of object constructor parameter combination leads to an exponential list of constructors. Instead of using numerous constructors, the builder pattern uses another object, a builder, that receives each initialization parameter step by step and then returns the resulting constructed object at once
So if I have an object requiring many construction parameters, and those parameters are required in a variety of combinations (thus making some parameters optional) then a builder is a good approach.
e.g. I could create multiple different constructors for an object, or I could do the following:
new ObjectBuilder().withParam1(1).withParam4(4).withParam19(19).build();
thus allowing me to select the parameters required, and not have to define many different constructors. Note also that the above can allow you to populate a builder, and set parameters/call build() multiple times to create a set of related objects easily.
Comments
Building an object in the builder class allows to encapsulate logic related to building an object in one place. Also you can deal more easily with dependencies required to for object construction than using regular constructor.
In other words - if constructing an object is complicated, and there are quite few dependencies - use builder pattern, and if object construction is straightforward (very little logics and just few parameters) - use constructor.
1 Comment
The constructor constructs the object you need, while the builder is a helper object to construct what you need. I think it is horrible!
- It contradicts the natural, well learnt method of object creation. What new methods will they come up with next?
- Instead of creating one object, you are forced to create two.
- It is absolutely not necessary! We always had to prepare variables for function params and we never used a builder.
eg:
// a class with ctor that takes lots of params
class Params
{
Params(int,int,int,String,char,boolean,int){}
}
// We dont need a builder.
// A builder will do the same following thing.
int p1 = 20;
int p2 = 40 + 40;
int p3 = (67 >> 1) * 3;
String p4 = "go" + "to" + "church";
char p5 = ' ' == 32 ? '5' : '7';
boolean p6 = true;
int p7 = MATCH_PARAMS;
new Params(p1,p2,p3,p4,p5,p6,p7);
//There is also this design pattern
new Params(
20,
40 + 40,
(67 >> 1) * 3,
"go" + "to" + "church",
' ' == 32 ? '5' : '7',
true,
MATCH_PARAMS
);
At the end of the day if you are a fan of builders no problem. But when Android makes constructors deprecated I don't like that. It like, type type type; deprecated.