You seem to have two different requirements, based on the method calls you provided.
- Only one (required) engine, only one (required) transmission, and only one (optional) stereo.
- One or more (required) engines, one or more (required) transmissions, and one or more (optional) stereos.
I think the first issue here is that you don't know what you want the class to do. Part of that is that it's not known what you want the built object to look like.
A car can only have one engine and one transmission. Even hybrid cars only have one engine (perhaps a GasAndElectricEngine
)
I'll address both implementations:
public class CarBuilder {
public CarBuilder(Engine engine, Transmission transmission) {
// ...
}
public CarBuilder setStereo(Stereo stereo) {
// ...
return this;
}
}
and
public class CarBuilder {
public CarBuilder(List<Engine> engines, List<Transmission> transmission) {
// ...
}
public CarBuilder addStereo(Stereo stereo) {
// ...
return this;
}
}
If an engine and transmission are required, then they should be in the constructor.
If you don't know what engine or transmission is required, then don't set one yet; it's a sign that you're creating the builder too far up the stack.
A key to the understanding of the GoF design patterns is their intent. For the builder it is:
Separate the construction of a complex object from its representation
so that the same construction process can create different
representations
Simplified variant
When looking at the solution proposed by GoF, what strikes most is the separate building and assembly of parts: the structure distinguishes the Director
and the Builder
and enables a step by step construction:
- This aspect of the pattern apparently stroke Joshua Bloch as well, because his approach uses this separation to set a lot of optional parameters.
- This sequence of events also allows for validating parts before assembling the result, as you've notice in this XmlBuilder.
While being very useful, I think however that these examples focus only on one aspect of the pattern and miss the real intent. Thus their structure appears simplified by merging either Director
and Builder
or Builder
and ConcreteBuilder
.
The missing intent
For GoF, it's not only about the separation between Director
and Builder
to build by step, but also the separation between Builder
and ConcreteBuilded
:
- If you look well at pattern structure, you'll notice that the
Builder
provides the polymorphic/virtual buildPart()
methods. But getResult()
to obtain the final product is a method of ConcreteBuilder
that is not included in the abstract Builder
's interface. So it can use different parameters or return different types.
- The GoF sequence diagram on page 99 shows furthermore that a
Client
calls the Director
for constructing the object and trigger the building of parts (perhaps using temporary objects), but that it's up to the client to call getResult()
directly from ConcreteBuilder.
- In fact, the client knows the ConcreteBuilder (may be even it instantiates it) and calls the
Director
by referfencing it.
This makes clear that this pattern is not so for building the parts and assemble the whole (which after all is nothing more than a helper to simplify providing all the elements at once), but more for returning very different "representations" (classes) using the same procedure for constructing the parts.
Real life use of the complete pattern
The typical use in real life is to build composite objects having different types of implementation/representation, such as for example different graphical representations/views (e.g. Entity/Relationship or UML model), different input or output formats (e.g. JSON or XML document) or encodings (e.g ASCII vs UTF16/32).
A concrete use could for example be ADO.net with DbCommandBuilder
that is implemented by concrete SqlCommandBuilder
, OracleCommandBuilder
, ODBCCommandBuilder
to generate db specific commands.
Best Answer
Builders are most useful when your object needs a lot of arguments/dependencies to be useful, or you want to allow many different ways of constructing the object.
Off the top of my head, I can imagine someone might want to "build" objects in a 3D game like this:
I would argue this example is more readable with the builder methods I made up just now than it would be with optional parameters:
In particular, the information implied by the builder method names has to get replaced by yet more parameters, and it's much easier to forget about one parameter in a group of closely related parameters. In fact, the fragment shader is missing, but you wouldn't notice that unless you knew to look for it.
Of course, if your object only takes one to five arguments to construct, there's no need to get the builder pattern involved, whether or not you have named/optional parameters.