The Builder pattern is an easy way to make your code more readable. The pattern is useful when dealing with a constructor that takes several arguments that aren’t easy to keep straight in your head. It is even more useful if the class has multiple constructors with different sets of arguments, or arguments in a different order.
As an example, an Order class might have the following constructor:
public class Order {
public Order(long price, int decimalPlaces, int shares, int side, String symbol) {
this.price = price;
...
}
}
A test for the Order class might look like:
@Test
public void testOrdersAreUnique() {
Order order1 = new Order(5000, 2, 4000, 2, "MSFT");
Order order2 = new Order(5000, 2, 4000, 2, "MSFT");
assertThat(order1).isNotEqualTo(order2);
}
It’s not obvious when reading the test what the different constructor arguments mean. Compare with the following:
@Test
public void testOrdersAreUnique() {
Order order1 = new OrderBuilder()
.withPrice(5000)
.withDecimalPlaces(2)
.withShares(4000)
.withSide(2)
.withSymbol("MSFT")
.build();
Order order2 = new OrderBuilder()
.withPrice(5000)
.withDecimalPlaces(2)
.withShares(4000)
.withSide(2)
.withSymbol("MSFT")
.build();
assertThat(order1).isNotEqualTo(order2);
}
The second version is a big improvement. By reading the code you know exactly what attributes the Orders have without needing to refer to the Order class definition. Another benefit of this pattern is that if a new parameter is added to the Order constructor down the road, that change does not cause the lines of code that use the OrderBuilder to change, the way they typically would if they invoked the constructor directly.
Implementing the Builder pattern is straightforward. OrderBuilder can be implemented as:
public class OrderBuilder {
private long price;
private int decimalPlaces;
private int shares;
private int side;
private String symbol;
public OrderBuilder withPrice(long price) {
this.price = price;
return this;
}
public OrderBuilder withDecimalPlaces(int decimalPlaces) {
this.decimalPlaces = decimalPlaces;
return this;
}
public OrderBuilder withShares(int shares) {
this.shares = shares;
return this;
}
public OrderBuilder withSide(int side) {
this.side = side;
return this;
}
public OrderBuilder withSymbol(String symbol) {
this.symbol = symbol;
return this;
}
public Order build() {
return new Order(price, decimalPlaces, shares, side, symbol);
}
}
Give it a try and see if it improves the readability of your code.
In my next post I’ll talk about using the Builder pattern to make tests easier to read and write.