Builder Design Pattern in Java

The Builder Design Pattern provides one of the best ways to create an object and it comes under creational design pattern. The builder pattern is an object creation software design pattern. Instead of using numerous constructors, in case of abstract factory pattern and factory method or design pattern, the builder pattern uses another object, a builder, that receives each initialization parameter step by step and then returns the resulting constructed object at once.

The definition in original Gang of Four book is given below

Allows for object level access control by acting as a pass through entity or a placeholder object.

Class Diagram

The class diagram for the builder design pattern is given in the below image:

Builder Design Pattern in Java

Problem

Take an example, a class with a list of constructors where each addition adds a new option parameter to the constructors.

Cake(double sugar) { ... }
Cake(double sugar, double butter) { ... }
Cake(double sugar, double butter, double flour) { ... }
Cake(double sugar, double butter, double flour, double bakingpowder) { ... }
Cake(double sugar, double butter, double flour, double bakingpowder, int eggs) { ... }
Cake(double sugar, double butter, double flour, double bakingpowder, int eggs, int vanila) { ... }
Cake(double sugar, double butter, double flour, double bakingpowder, int eggs, int vanila, double milk) { ... }
Cake(double sugar, double butter, double flour, double bakingpowder, int eggs, int vanila, double milk, int cherry) { ... }

This is called the Telescoping Constructor Pattern. The problem with this pattern is that once constructors are 4 or 5 parameters long it becomes difficult to remember the required order of the parameters as well as what particular constructor you might want in a given situation.

One alternative we have to the Telescoping Constructor Pattern is the Java Bean Pattern where you need to call a constructor with the mandatory parameters and then call any optional setters after:

Cake cake = new Cake(250.0);
cake.setButter(100.0);
cake.setFlour(500.0);
cake.setBakingpowder(50.0);

The problem here is, because the object is created over several calls it may be in an inconsistent state partway through its construction. This also requires a lot of extra effort to ensure thread safety. Therefore the better alternative approach is to use builder design pattern.

The builder design pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters. For example imagine a DOM (Document Object Model). You have to create plenty of nodes and attributes to get our final object. A factory is used when the factory can easily create the entire object within one method call.

Advantages of Builder Pattern

  • More maintainable if number of fields required to create object is more than 4 or 5.
  • Less error-prone as user will know what they are passing because of explicit method call.
  • More robust as only fully constructed object will be available to the client.

Disadvantages of Builder Pattern

  • Verbose and code duplication as Builder pattern needs to copy all fields from original class.

Builder Pattern Implementation

Now I am going to implement the builder design pattern. I am going to make two types of cake – veg and non-veg. So here you will see how I am going to implement this using builder pattern.

To make a Cake we will follow several steps:

  • mix ingredients
  • bake
  • frost

In the below example, I will create an abstract class CakeBuilder to define these three steps. Any class which extends CakeBuilder will follow the same steps to make a cake.

package com.roytuts.java.builder.design.pattern;

public abstract class CakeBuilder {

	protected Cake cake;
	protected Ingredients ingredients;
	protected Bake bake;
	protected Frost frost;

	public abstract Cake createCake();

	public abstract Ingredients mixIngredients();

	public abstract Bake bakeMixedIngredients();

	public abstract Frost frostCake();

}
package com.roytuts.java.builder.design.pattern;

public interface Ingredients {

	String getIngredients();

}
package com.roytuts.java.builder.design.pattern;

public interface Bake {

	String getBaked();

}
package com.roytuts.java.builder.design.pattern;

public interface Frost {

	String getFrost();

}
package com.roytuts.java.builder.design.pattern;

public abstract class Cake {

	protected Ingredients ingredients;
	protected Bake bake;
	protected Frost frost;

	public Ingredients getIngredients() {
		return ingredients;
	}

	public void setIngredients(Ingredients ingredients) {
		this.ingredients = ingredients;
	}

	public Bake getBake() {
		return bake;
	}

	public void setBake(Bake bake) {
		this.bake = bake;
	}

	public Frost getFrost() {
		return frost;
	}

	public void setFrost(Frost frost) {
		this.frost = frost;
	}

	public abstract String getCake();

}

Then I will use CakeDirector class to force the order of these three steps, i.e., I have to first mix ingredients, then bake and at last we have to frost the made cake.

package com.roytuts.java.builder.design.pattern;

public class CakeDirector {

	public Cake makeCake(CakeBuilder cakeBuilder) {
		Cake cake = cakeBuilder.createCake();
		System.out.println(cake.getCake());

		cake.setIngredients(cakeBuilder.mixIngredients());
		System.out.println(cake.getIngredients().getIngredients());

		cake.setBake(cakeBuilder.bakeMixedIngredients());
		System.out.println(cake.getBake().getBaked());

		cake.setFrost(cakeBuilder.frostCake());
		System.out.println(cake.getFrost().getFrost());

		return cake;
	}

}

The CakeClient orders the cake of two types, one is Veg Cake and another one is Non-Veg Cake.

package com.roytuts.java.builder.design.pattern;

public class VegCake extends Cake {

	@Override
	public String getCake() {
		return "Make a Veg Cake";
	}

}
package com.roytuts.java.builder.design.pattern;

public class VegCakeIngredients implements Ingredients {

	@Override
	public String getIngredients() {
		return "Get ingredients for Veg Cake";
	}

}
package com.roytuts.java.builder.design.pattern;

public class VegCakeBake implements Bake {

	@Override
	public String getBaked() {
		return "Bake the Veg Cake at 350 degree centigrade";
	}

}
package com.roytuts.java.builder.design.pattern;

public class VegCakeFrost implements Frost {

	@Override
	public String getFrost() {
		return "Cool and frost the Veg Cake as desired";
	}

}
package com.roytuts.java.builder.design.pattern;

public class VegCakeBuilder extends CakeBuilder {

	@Override
	public Cake createCake() {
		return new VegCake();
	}

	@Override
	public Ingredients mixIngredients() {
		return new VegCakeIngredients();
	}

	@Override
	public Bake bakeMixedIngredients() {
		return new VegCakeBake();
	}

	@Override
	public Frost frostCake() {
		return new VegCakeFrost();
	}

}
package com.roytuts.java.builder.design.pattern;

public class NonVegCake extends Cake {

	@Override
	public String getCake() {
		return "Make a Non-Veg Cake";
	}

}
package com.roytuts.java.builder.design.pattern;

public class NonVegCakeIngredients implements Ingredients {

	@Override
	public String getIngredients() {
		return "Get ingredients for Non-Veg Cake";
	}

}
package com.roytuts.java.builder.design.pattern;

public class NonVegCakeBake implements Bake {

	@Override
	public String getBaked() {
		return "Bake the Non-Veg Cake at 400 degree centigrade";
	}

}
package com.roytuts.java.builder.design.pattern;

public class NonVegCakeFrost implements Frost {

	@Override
	public String getFrost() {
		return "Cool and frost the Non-Veg Cake as desired";
	}

}
package com.roytuts.java.builder.design.pattern;

public class NonVegCakeBuilder extends CakeBuilder {

	@Override
	public Cake createCake() {
		return new NonVegCake();
	}

	@Override
	public Ingredients mixIngredients() {
		return new NonVegCakeIngredients();
	}

	@Override
	public Bake bakeMixedIngredients() {
		return new NonVegCakeBake();
	}

	@Override
	public Frost frostCake() {
		return new NonVegCakeFrost();
	}

}

Even though cakes are of different types but they are made in the same way. The making process allows different representation for the object that is created.

package com.roytuts.java.builder.design.pattern;

public class CakeClient {

	public static void main(String[] args) {
		CakeDirector cakeDirector = new CakeDirector();

		// make a veg cake
		VegCakeBuilder vegCakeBuilder = new VegCakeBuilder();
		cakeDirector.makeCake(vegCakeBuilder);
		System.out.println();

		// make a non-veg cake
		NonVegCakeBuilder nonVegCakeBuilder = new NonVegCakeBuilder();
		cakeDirector.makeCake(nonVegCakeBuilder);
	}

}

Run the class CakeClient and you will see the below output:

Make a Veg Cake
Get ingredients for Veg Cake
Bake the Veg Cake at 350 degree centigrade
Cool and frost the Veg Cake as desired

Make a Non-Veg Cake
Get ingredients for Non-Veg Cake
Bake the Non-Veg Cake at 400 degree centigrade
Cool and frost the Non-Veg Cake as desired

Source Code

Download

That’s all. Thanks for reading.

0 thoughts on “Builder Design Pattern in Java

  1. Great and informative article on builder design pattern in java. Its good to know the concept of this builder design patter with good example that you have shared. As a java beginner it is very helpful to go through this precise and understanding article, thanks a lot!

Leave a Reply

Your email address will not be published. Required fields are marked *