Achieving Immutability with Builder Design Pattern
Immutable classes define objects which, once created, never change their state.
Join the DZone community and get the full member experience.
Join For FreeOne piece of advice from Effective Java is that you should make your classes Immutable unless there is a good reason to make them mutable. If a class cannot be made immutable, limit its mutability as much as possible. Immutable classes define objects which, once created, never change their state. All the state information is provided at the time the object is constructed and it does not change for the lifetime of the object.
Why should we write Immutable classes?
Immutable classes offer many advantages over mutable classes. These are:
- An immutable object is simple and easier to use as it can be in only one state, the state in which it was created.
- They are inherently thread safe, i.e. they do not require synchronization.
- The objects of immutable classes can be shared freely. For example, Boolean class reuses its existing instances TRUE and FALSE and whenever you call Boolean.valueOf method it gives you the already created instances.
Creating an Immutable class
A simple immutable class can be like this:
public final class User {private final String username;private final String password;public User(String username, String password) {this.username = username;this.password = password;}public String getUsername() {return username;}public String getPassword() {return password;}}
This class is immutable because:
- It does not provide setter methods or mutators.
- The Class can't be extended because it is final. This could have also been done by making the constructor private.
- Fields of the class are all final and private.
It is important to note that this class was very simple with only two fields. In most of the classes in our real applications there are more than two fields. Also, most of these fields are not mandatory for object creation. For example, a user in a real application will have a username, password, firstname, lastname, creationDate, emailAddress, etc., but for user creation here, only a username and password are required. So, we design our class as shown below:
public final class User {private final String username;private final String password;private String firstname;private String lastname;private String email;private Date creationDate;public User(String username, String password) {this.username = username;this.password = password;creationDate = new Date();}public String getUsername() {return username;}public String getPassword() {return password;}public String getFirstname() {return firstname;}public void setFirstname(String firstname) {this.firstname = firstname;}public String getLastname() {return lastname;}public void setLastname(String lastname) {this.lastname = lastname;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public Date getCreationDate() {return new Date(creationDate.getTime());}}
This class is not immutable because it has mutators, i.e. setters. So, instances of this class can be modified after creation. This approach has the disadvantage that objects can be in an inconsistent state part of the way through their construction and you have to put in extra effort to ensure thread safety.
Builder Design Pattern comes to the Rescue
According to Gof:
The Builder Pattern separates the construction of a complex object from its representation so that the same construction process can create different representations.
The builder design pattern provides a way for you to build complex immutable objects. The process is:
- The client calls a constructor (or static factory) with all the required parameters and gets a builder object.
- The client calls setter like methods to set each optional parameter of interest.
- Finally the client calls the build method to generate the object which is immutable.
Immutable User
public class ImmutableUser {private final String username;private final String password;private final String firstname;private final String lastname;private final String email;private final Date creationDate;private ImmutableUser(UserBuilder builder) {this.username = builder.username;this.password = builder.password;this.creationDate = builder.creationDate;this.firstname = builder.firstname;this.lastname = builder.lastname;this.email = builder.email;}public static class UserBuilder {private final String username;private final String password;private final Date creationDate;private String firstname;private String lastname;private String email;public UserBuilder(String username, String password) {this.username = username;this.password = password;this.creationDate = new Date();}public UserBuilder firstName(String firsname) {this.firstname = firsname;return this;}public UserBuilder lastName(String lastname) {this.lastname = lastname;return this;}public UserBuilder email(String email) {this.email = email;return this;}public ImmutableUser build() {return new ImmutableUser(this);}}public String getUsername() {return username;}public String getPassword() {return password;}public String getFirstname() {return firstname;}public String getLastname() {return lastname;}public String getEmail() {return email;}public Date getCreationDate() {return new Date(creationDate.getTime());}}
You should also check the invariants in the build method and throw in an IllegalStateException if any attribute is invalid. This will ensure that the object is in a workable state once it is instantiated.
The client code will look like this :
public static void main(String[] args) {ImmutableUser user = new ImmutableUser.UserBuilder("shekhar","password").firstName("shekhar").lastName("gulati").email("shekhargulati84@gmail.com").build();}
In this way you build a complex object which is immutable and has all the advantages of immutable objects.
Note : Effective Java is a must read for every Java developer
Opinions expressed by DZone contributors are their own.
Comments