As Java developers, we are familiar with constructors. We create them every day to get our objects into a usable state.
Consider a simple Rectangle class's constructor.
class Rectangle{
private int x,y,w,h; // x, y, width and height
Rectangle(int _x, int _y, int _w, int _h){
// Intialize x, y, w and h
}
}
As API developers, we are always attempting to balance functionality and convenience. Even this fairly straightforward class can become confusing when we try to provide convenient alternative constructors.
If, for example, our Rectangle's w and h were usually 100 we may be tempted to create a constructor which defers to the original constructor with default values.
Rectangle(int _x, int _y){
this(_x, _y, 100, 100);
}
One might also allow a default for when h and w are 100 and x and y are 0
Rectangle(){
this(0, 0, 100, 100);
}
Things get tricky when we then try to create a constructor where we specify the w and h and take the default x and y.
We might be tempted to try to add this constructor
Rectangle(int _w, int _h){
this(0, 0, _w, _h);
}
Sadly, this constructor is now ambiguous and will result in a compile failure. If you look at Rectangle(int _x, int _y), it is the same. The compiler does not care what the variable names are, there can only be one constructor taking (int,int).
It would be great if Java had a syntax for disambiguating the arguments to a method or constructor.
Rectangle r = new Rectangle(width:100, x:20);
We could specify the arguments in any order and the compiler would create a synthetic constructor which matched the arguments; alas Java cannot do this although we will see later how we can get close to this.
Of course, if we did not want our Rectangle to be immutable, we can abandon having multiple constructors and just use mutators.
Rectangle r = new Rectangle(); // defaults x,y = 0 and width,height=100
r.setWidth(500);
r.setHeight(500);
This is probably the best approach for most classes; it is a little verbose but it is easy to understand.
However, for immutable classes we need another approach.
Thankfully, we can use a builder pattern.
If we define an inner static class within Rectangle called Builder which we can mutate into an appropriate state (using regular setters), we can then pass this builder into Rectangle's constructor and let the constructor pull its own state from the builder.
Sounds more complicated than it is. So lets see what this looks like:
class Rectangle{
private int x,y,h,w;
public static class Builder{
protected int x,y,h,w;
void setW(int _w){w = _w; }
void setX(int _x){x = _x;}
void setY(int _y){y = _y;}
void setH(int _h){h = _h;}
}
public Rectangle(Builder _builder){
x=_builder.x;
y=_builder.x;
w=_builder.w;
h=_builder.h;
}
}
So now we can create an instance of Rectangle.Builder and mutate it.
Then we can create a Rectangle by passing the Rectangle.Builder to the constructor.
Rectangle.Builder builder = new Rectangle.Builder();
builder.setW(500);
builder.setH(1000);
Rectangle rectangle = new Rectangle(builder);
We can make code less verbose by using what I will call 'chainable mutators' to the builder. Unlike traditional mutators, we avoid using the traditional setX() style for field x;instead we just name the mutator the same as the field (so we use x() instead ofsetX()). We also return 'this' from our mutator so that subsequent mutator calls can be chained together.
Our builder mutators now look like this:
Builder w(int _w){
w = _w;
return(this);
}
And so now we can use:
Rectangle.Builder builder = new Rectangle.Builder();
builder.w(200).h(200).x(5).y(5);
Rectangle rectangle = new Rectangle(builder);
We can construct and chain in one line:
Rectangle.Builder builder = new Rectangle.Builder().w(200).h(200).x(5).y(5);
Rectangle rectangle = new Rectangle(builder);
Or we can avoid declaring the temporary builder object altogether.
Rectangle r = new Rectangle(new Rectangle.Builder().w(200).h(200).x(5).y(5));
Now we have the safety of immutable Rectangles with a mechanism for getting immutable objects into a reasonable state.
At this point we can make two more enhancements.
1. Add a static build() method to Rectangle to construct the Rectangle.Builder.
2. Add a commit() method to the Rectangle.Builder to construct the Rectangle.
Our code now looks like this:
class Rectangle{
private int x,y,h,w;
public static class Builder{
protected int x,y,h,w;
Builder w(int _w){w = _w; return(this);}
Builder x(int _x){x = _x; return(this);}
Builder y(int _y){y = _y; return(this);}
Builder h(int _h){h = _h; return(this);}
Rectangle commit(){return new Rectangle(this);}
}
private Rectangle(Builder _builder){
x=_builder.x;
y=_builder.x;
w=_builder.w;
h=_builder.h;
}
Builder build(){
return(new Builder());
}
}
And we can create a Rectangle using:
Rectangle r = Rectangle.build().w(200).h(200).x(5).y(5).commit();
This pattern is certainly not new. It borrows from the traditional builder pattern and from what Martin Fowler refers to as a 'fluent interface': (http://martinfowler.com/bliki/FluentInterface.html).
We have been using this pattern in internal projects and would be interested to hear how folks feel about combining the builder pattern with chainable mutators for constructing immutable objects.
-------------------------
The information presented in this document is for informational purposes only and may contain technical inaccuracies, omissions and typographical errors. Links to third party sites are for convenience only, and no endorsement is implied
Edited: 02/16/2009 at 06:27 PM by AMD Developer Blogs Moderator