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:
// Just ignore the fact that this hypothetical god class is coupled to everything ever
new ObjectBuilder(x, y, z).importBlenderMesh("./meshes/foo")
.syncWithOtherPlayers(serverIP)
.compileShaders("./shaders/foo.vert", "./shaders/foo.frag")
.makeDestructibleRigidBody(health, weight)
...
I would argue this example is more readable with the builder methods I made up just now than it would be with optional parameters:
new Object(x, y, z, meshType: MESH.BLENDER,
meshPath: "./meshes/foo",
serverToSyncWith: serverIP,
vertexShader: "./shaders/foo.vert",
physicsType: PHYSICS_ENGINE.RIGID_DESTRUCTIBLE,
health: health,
weight: weight)
...
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.
And, besides what Ross Patterson suggested, consider this position which is the exact opposite:
Take maxims such as "Thou Shalt Not Do Any Real Work In Thy Constructors" with a grain of salt.
A constructor is, really, nothing but a static method. So, structurally, there is really not much difference between:
a) a simple constructor and a bunch of complex static factory methods, and
b) a simple constructor and a bunch of more complex constructors.
A considerable part of the negative sentiment towards doing any real work in constructors comes from a certain period of the history of C++ when there was debate as to precisely what state the object will be left in if an exception is thrown within the constructor, and whether the destructor should be invoked in such an event. That part of the history of C++ is over, and the issue has been settled, while in languages like Java there never was any issue of this kind to begin with.
My opinion is that if you simply avoid using new
in the constructor, (as your intention to employ Dependency Injection indicates,) you should be fine. I laugh at statements like "conditional or looping logic in a constructor is a warning sign of a flaw".
Besides all that, personally, I would take the XML parsing logic out of the constructor, not because it is evil to have complex logic in a constructor, but because it is good to follow the "separation of concerns" principle. So, I would move the XML parsing logic into some separate class altogether, not into some static methods that belong to your ModelDef
class.
Amendment
I suppose that if you have a method outside of ModelDef
which creates a ModelDef
from XML, you will need to instantiate some dynamic temporary tree data structure, populate it by parsing your XML, and then create your new ModelDef
passing that structure as a constructor parameter. So, that could perhaps be thought of as an application of the "Builder" pattern. There is a very close analogy between what you want to do and the String
& StringBuilder
pair. However, I have found this Q&A which seems to disagree, for reasons which are not clear to me: Stackoverflow - StringBuilder and Builder Pattern. So, to avoid a lengthy debate over here as to whether the StringBuilder
does or does not implement the "builder" pattern, I would say feel free to be inspired by how StrungBuilder
works in coming up with a solution that suits your needs, and postpone calling it an application of the "Builder" pattern until that little detail has been settled.
See this brand new question: Programmers SE: Is “StringBuilder” an application of the Builder Design Pattern?
Best Answer
A
StringBuilder
is similar to a the Builder Pattern, but does not share much with the GoF description of this design pattern. The original point of the design pattern was(note: “complex” primarily means “composed of multiple parts”, not necessarily “complicated” or “difficult”)
The “different representations” is key here. E.g. assuming this construction process:
we might end up with a
HtmlDocument
or aTexDocument
or aMarkdownDocument
depending on what concrete implementation is provided:So one central point of the Builder pattern is polymorphism. The Design Patterns book compares this pattern to the Abstract Factory:
This step-by-step aspect has then become the more popular aspect of the Builder pattern, so that in common parlance the Builder pattern is understood like this:
Wikipedia defines the pattern like this:
So as we can see, there is no truly common understanding of which pattern this name refers to, and in some points different definitions even contradict one another (e.g. regarding the relevance of polymorphism for Builders).
The only common property of the
StringBuilder
with various interpretations of the pattern is that the product is created step by step rather than in one go. It does not meet a strict reading of the GoF definition of the design pattern, but please note that design patterns are malleable concepts meant to facilitate communication. I would continue to callStringBuilder
an example of the Builder Pattern, albeit an atypical one – the main reason for that structure in Java is performant concatenation in the presence of immutable strings, but not some interesting object-oriented design.