Article: Using Builders instead of Constructors to create Immutable objects
This article explains how you can create immutable objects in Java by using the builder design pattern as opposed to constructors. This will make your code more readable. Firstly, let's see what is not so elegant about using a constructor as shown below with a CashBalance object that takes 3 BigDecimal arguments. Then we will see how a builder class will make your code more intuitive.
Next time you are asked to explain a design pattern in an interview, you could pick this as opposed to the very common factory and singleton design patterns. This article also touch on other two important concepts like immutability and thread-safety. |
import java.math.BigDecimal; /** * Immutable, hence thread safe CashBalance object */ public final class CashBalance { private BigDecimal initialBalance; private BigDecimal totCredits; private BigDecimal totDebits; //construct public CashBalance(BigDecimal initialBalance, BigDecimal totCredits, BigDecimal totDebits) { this.initialBalance = initialBalance; this.totCredits = totCredits; this.totDebits = totDebits; } //only getter methods and not setter methods as it is an immutable object }
So, why wasn't it elegant? The constructor takes 3 BigDecimal arguments, and for the user of the class it is not intuitive enough as to which is initialBalance, which is totCredits, etc. It would have been nicer if you could invoke the constructor something like
CashBalance bal = new CashBalance(initialBalance:BigDecimal.valueOf(250.00), totCredits:BigDecimal.valueOf(250.00), totDebits:BigDecimal.valueOf(250.00));
Unfortunately, you can't use above syntax. You need to invoke it as shown below.
CashBalance bal = new CashBalance(BigDecimal.valueOf(250.00), BigDecimal.valueOf(250.00), BigDecimal.valueOf(250.00));
Constructing the object with an empty constructor and 3 setter methods are more elegant, but that will make your object mutable. Here is the builder design pattern to the rescue. Here is the builder defined as an inner class to construct the CashBalance object.
import java.math.BigDecimal; /** * Immutable, hence thread safe CashBalance object */ public final class CashBalance { private BigDecimal initialBalance, totCredits, totDebits; //construct public CashBalance(CashBalanceBuilder builder) { this.initialBalance = builder.initialBalance; this.totCredits = builder.totCredits; this.totDebits = builder.totDebits; } //builder design pattern public static class CashBalanceBuilder { //has same fields as the object it is going to build protected BigDecimal initialBalance, totCredits, totDebits; //define the setters with package private access void setInitialBalance(BigDecimal initialBalance) { this.initialBalance = initialBalance; } void setTotCredits(BigDecimal totCredits) { this.totCredits = totCredits; } void setTotDebits(BigDecimal totDebits) { this.totDebits = totDebits; } } //only getter methods and not setter methods as it is an immutable object }Now, you can construct the CashBalance object from outside the class as shown below.
public static void main(String[] args) { CashBalance.CashBalanceBuilder builder = new CashBalance.CashBalanceBuilder(); builder.setInitialBalance(BigDecimal.valueOf(250.00)); builder.setTotCredits(BigDecimal.valueOf(250.00)); builder.setTotDebits(BigDecimal.valueOf(250.00)); CashBalance bal = new CashBalance(builder); }
The above code does the job, but if you have many number of fields, the construction code will be very verbose. This can be further improved as shown below.
The following improved method changes the void setter methods to return the builder itself after setting the value.
import java.math.BigDecimal; /** * Immutable, hence thread safe CashBalance object */ public final class CashBalance { private BigDecimal initialBalance, totCredits, totDebits; //construct public CashBalance(CashBalanceBuilder builder) { this.initialBalance = builder.initialBalance; this.totCredits = builder.totCredits; this.totDebits = builder.totDebits; } public static class CashBalanceBuilder { //has same fields as the object it is going to build protected BigDecimal initialBalance, totCredits, totDebits; //define the setters that return itself CashBalanceBuilder setInitialBalance(BigDecimal initialBalance) { this.initialBalance = initialBalance; return this; } CashBalanceBuilder setTotCredits(BigDecimal totCredits) { this.totCredits = totCredits; return this; } CashBalanceBuilder setTotDebits(BigDecimal totDebits) { this.totDebits = totDebits; return this; } } //only getter methods and no setter methods as it is an immutable object }
The beauty of the change is that now the oCashBalance construction code becomes:
public static void main(String[] args) { CashBalance.CashBalanceBuilder builder = new CashBalance.CashBalanceBuilder() .setInitialBalance(BigDecimal.valueOf(250.00)) .setTotCredits(BigDecimal.valueOf(250.00)) .setTotDebits(BigDecimal.valueOf(250.00)); CashBalance bal = new CashBalance(builder); }
So, these kinds of "know hows" will be noticed in peer code reviews and will help you earn a reputation as a go to person.
Similar post:
Labels: design pattern
4 Comments:
Thanks.
But nothing is explained about thread safety.
Immutable classes are inherently thread-safe.
Arun, You have explained it nicely. Thanks.
Thank you one of the best explanations I've ever read for this design pattern
Post a Comment
Subscribe to Post Comments [Atom]
<< Home