The purpose of the builder pattern is to simplify the construction of complex objects, providing us a method to create different representations by using the same construction process.
It’s not uncommon to create objects that need to receive lots of arguments to be properly initialized and in a usable state.
There are at least two ways to achieve this. We can Use a parametrized constructor or we can use setters.
Let’s see the problems that can arise from this “standard” way to do things.
The constructor problem
let’s imagine you are examining some code and come across a line like,
Window window = new Window (100,100,200,300,"settings",35,127,32)
What information do you get from there ? What are all those values for ?
We don’t know, so, although the IDE may help a little here, we normally end up visiting the Window class, just to make sure. It may be simple, and surely works, but the true is that an extra layer of complexity was inserted in this code, as it is not easily understandable.
The setter’s problem
Although using setters will overcome the problem of us not knowing what the values refer to, there is a severe hidden problem.
To access the setters, we first need to instantiate the object, and that means that the object already exists somewhere in the heap, but until we have all the setters “properly setted”, the object is in an inconsistent state. Using it, before it is fully formed, may lead to unpredictable results. In some applications, this is a disaster waiting to happen.
There must be a better way, and that’s where the builder pattern comes in.
The idea is to build our object, step by step, and in the end we will receive the object fully formed.
The steps we will use will be,
- Create a class that represents our object with all the needed attributes.
- Create a static inner class an in this inner class we copy all the attributes of the outer class. Convention dictates that the inner class should be named as the outer class with the suffix builder.
- The builder class should have a public constructor with all attributes as parameters.
- In the inner class we should make available a public method named builder (again, this name is a convention) that will return our object. To properly achieve this we will use a private constructor in the outer class that will receive as argument it’s inner class.
NOTE : we may have more than one builder class, each one with different steps to create the object.
Here we are using the fact that in Java, a static inner class acts as a static member of the outer class, and it can be accessed without instantiating the outter class.
After implementing a builder, the line we showed above as,
Window window = new Window (100,100,200,300,"settings",35,127,32)
will become
`Window window = new Window.WindowBuilder()
.withLeftTopcornerPositionX(100)
.withLeftTopCornerPositionY(100)
.withHeight(300)
.withWidth(600)
.fillWithRGB(125,180,28)
.withTitle("Settings")
.build();`
Now, besides coding, we are actually communicating in an elegant way. Anyone that comes across this code, will easily understand what it does.
So, let’s see the implementation,
1 | package BuilderPattern; |
Now, that may seems a lot of work, but trust me, it will pay off in the long run.
An example for the code in this article can be found in git-hub