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.)