Best Practices - Using builder pattern when faced with many constructors paramters

Static factories and constructors share a limitation, they do not scale well for large number of optional parameters. Consider an example where you have a class representing a Book. It has few required fields such a title, author, publisher, published date and over 10 optional fields.

Traditional approach would be to have:

  1. one constructor with required fields
  2. second constructor with required fields and one optional field
  3. third and so on with required fields and combination of optional fields

This is traditional telescoping constructor pattern. Here is how code looks:

public class Book {

	private String title;			 //required
	private String author;			 //required
	private String publisher;		 //required
	private Date publishedDate;		 //required
	private String publishedCountry; //optional
	private String primaryLang;		 //optional

	public Book(String title, String author, String publisher,
			Date publishedDate) {
		this.title = title;
		this.author = author;
		this.publisher = publisher;
		this.publishedDate = publishedDate;
	}
	
	public Book(String title, String author, String publisher,
			Date publishedDate, String publishedCountry,
			String primaryLang, String publishedCountry) {
		this.title = title;
		this.author = author;
		this.publisher = publisher;
		this.publishedDate = publishedDate;
		this.publishedCountry = publishedCountry;
		this.primaryLang = primaryLang;
		this.publishedCountry = publishedCountry;
	}
	
}

// using 1st constructor 
Book book = new Book("Learn java", "james", "TMH", new Date(02/13/2001));
// using 2nd constructor
Book book = new Book("Learn java", "james", "TMH", new Date(02/13/2001), "USA", "EN");
// using 2nd constructor 
Book book = new Book("Learn java", "james", "TMH", new Date(02/13/2001), null, "EN"); 

To create objects

In the 3rd case you end up passing parameter which is not required. This may not look bad with 6 parameters, but imagine having 10 parameters and passing "" or null for the optional fields. Also passing the parameters in such a fashion is a recipe for introducing bugs in the code. For e.g one could forgot to pass one parameter and the constructor is still present or passing the parameters in wrong order.

The second way is the JavaBean patterms, which allows to create a parameterless constructor and then call setters method to set each field. This pattern is bit wordy to create instances but easy to read. Unfortunately the pattern has major disadvantage, a JavaBean may be in an inconsistent state partway through its construction since multiple calls are involved for object creation. The class can not force the validity of the input parmeters via the constuctor. Here is how the code looks like:

public class Book {

	private String title;
	private String author;
	private String publisher;
	private Date publishedDate;
	private String publishedCountry;
	private String primaryLang;
	
	public void setTitle(String title) {
		this.title = title;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
	public void setPublisher(String publisher) {
		this.publisher = publisher;
	}
	public void setPublishedDate(Date publishedDate) {
		this.publishedDate = publishedDate;
	}
	public void setPublishedCountry(String publishedCountry) {
		this.publishedCountry = publishedCountry;
	}
	public void setPrimaryLang(String primaryLang) {
		this.primaryLang = primaryLang;
	}

}

Book b = new Book();
b.setAuthor("james gosling");
b.setTitle("Learning java");
b.setPusblisher("TMH");
...

There is third way that combines the advantages of the telescopic constructor pattern and readability of the JavaBeans pattern. It is Builder Pattern The client calls the constructor with all of required parameters and gets a builder object. Then the client calls the setter like method on the builder object to set each optional field. Finally client calls a parameterless build method to generate the object which is immutable. The builder is static member class of the class it builds. Here is how code looks:

public class Book {

	private String title;
	private String author;
	private String publisher;
	private Date publishedDate;
	private String publishedCountry;
	private String primaryLang;
	
	public static class Builder {
		//required
		private String title;
		private String author;
		private String publisher;
		private Date publishedDate;
		
		private String publishedCountry;
		private String primaryLang;
		
		public Builder(String title, String author, String publisher) {
			super();
			this.title = title;
			this.author = author;
			this.publisher = publisher;
			this.publishedDate = publishedDate;
		}
		
		public Builder publishedCountry(String publishedCountry) {
			this.publishedCountry = publishedCountry;
			return this;
		}
		
		public Builder primaryLang(String primaryLang) {
			this.primaryLang = primaryLang;
			return this;
		}
		
		public Book build() {
			return new Book(this);
		}
	}
	
	private Book(Builder builder) {
		this.title = builder.title;
		this.author = builder.author;
		this.primaryLang = builder.primaryLang;
		this.publishedCountry = builder.publishedCountry;
		this.publishedDate = builder.publishedDate;
		this.publisher = builder.publisher;
	}

}

Book book = new Book.Builder("Learn java", "james", "TMH", new Date(02/13/2001)).build();
Book book = new Book.Builder("Learn java", "james", "TMH", new Date(02/13/2001)).primaryLang("EN").build();

As you could see, it becomes easy to create object.

Like a constructor, a builder can impose invariants on its parameters. The build method can check these invariants. It is important to note the validation of parameters is done after copying the parameters from the builder to the object, and checked on the objects fields instead of the builder fields. The builder can fill in some fields automatically, such as serial number that increases each time object is created.

If you liked the article please share it or add comments.

Happy Coding !!

Subscribe to get latest updates.

© 2015 Java Questions | Sitemap