Sunday, July 1, 2007

A Java Builder Pattern

There's a Builder pattern that Joshua Bloch has briefly described in a couple of his "Effective Java Reloaded" sessions at Java One. This Builder is not necessarily a replacement for the original design pattern. The problems this Builder pattern can solve are too many constructors, too many constructor parameters, and over use of setters to create an object.


Here are some examples of the pattern in use. These examples create various Widgets with two required properties and several optional ones -




Widget x = new Widget.Builder("1", 1.0).
model("1").build();
Widget y = new Widget.Builder("2", 2.0).
model("2").manufacturer("222").
serialNumber("12345").build();
Widget z = new Widget.Builder("3", 4.0).
manufacturer("333").
serialNumber("54321").build();


The basic idea behind the pattern is to limit the number of constructor parameters and avoid the use of setter methods. Constructors with too many parameters, especially optional ones, are ugly and hard to use. Multiple constructors for different modes are confusing. Setter methods add clutter and force an object to be mutable. Here is an class skeleton of the pattern -



public class Widget {
public static class Builder {
public Builder(String name, double price) { ... }
public Widget build() { ... }
public Builder manufacturer(String value) { ... }
public Builder serialNumber(String value) { ... }
public Builder model(String value) { ... }
}

private Widget(Builder builder) { ... }
}


Notice that Widget has no public constructor and no setters and that the only way to create a Widget is using the static inner class Widget.Builder. Widget.Builder has a constructor that takes the required properties of Widget. Widget's optional properties can be set using optional property methods on the Widget.Builder. The property methods of Widget.Builder return a reference to the builder so method calls can be chained.


A really nice feature of this pattern is the ability to do pre-creation validation of an object state. When setters are used to set object state during creation it is virtually impossible to guarantee that object has been properly created.


Here is the full source for Widget and its Builder -



public class Widget {
public static class Builder {
private String name;
private String model;
private String serialNumber;
private double price;
private String manufacturer;

public Builder(String name, double price) {
this.name = name;
this.price = price;
}

public Widget build() {
// any pre-creation validation here
Widget result = new Widget(name, price);
result.model = model;
result.serialNumber = serialNumber;
result.manufacturer = manufacturer;
return result;
}

public Builder manufacturer(String value) {
this.manufacturer = value;
return this;
}

public Builder serialNumber(String value) {
this.serialNumber = value;
return this;
}

public Builder model(String value) {
this.model = value;
return this;
}
}

private String name;
private String model;
private String serialNumber;
private double price;
private String manufacturer;

/**
* Creates an immutable widget instance.
*/
private Widget(String name, double price) {
this.name = name;
this.price = price;
}

public String toString() {
return super.toString() + " {"
+ "name="
+ getName()
+ " model="
+ getModel()
+ " serialNumber="
+ getSerialNumber()
+ " price="
+ getPrice()
+ " manufacturer="
+ getManufacturer()
+ "}";
}

public String getManufacturer() {
return manufacturer;
}

public String getModel() {
return model;
}

public String getName() {
return name;
}

public double getPrice() {
return price;
}

public String getSerialNumber() {
return serialNumber;
}
}


Notice that Widget's private constructor takes the required properties and that the Builder sets the optional properties. Another thing to note is that widget is an immutable object as implemented.

238 comments:

«Oldest   ‹Older   401 – 238 of 238
«Oldest ‹Older   401 – 238 of 238   Newer› Newest»