DEV Community

Gaurav
Gaurav

Posted on • Updated on

Decode Builder Design Pattern

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

When to use

  • To avoid dealing with inconsistent object when the object needs to be created over several steps.
  • To avoid too many constructor arguments.
  • To construct an object that should be immutable.
  • To encapsulate the complete creation logic.

Intent

Separate the construction of a complex object from its representation so that the same construction process can create different representations.


Components

  1. The Builder class specifies an abstract interface for creating parts of a Product object.
  2. The ConcreteBuilder constructs and puts together parts of the product by implementing the Builder interface. It defines and keeps track of the representation it creates and provides an interface for saving the product.
  3. The Director class constructs the complex object using the Builder interface.
  4. The Product represents the complex object that is being built.

Structure

Builder Pattern


Implementation

1 Define the Product (House) that gets assembled in the builder pattern.

package com.gaurav.builder;

/* The house is the object that gets assembled in the builder pattern. */
public class House {

  private String floorType;
  private String wallType;
  private String roofType;

  public String getFloorType() {
    return floorType;
  }

  public void setFloorType(String floorType) {
    this.floorType = floorType;
  }

  public String getWallType() {
    return wallType;
  }

  public void setWallType(String wallType) {
    this.wallType = wallType;
  }

  public String getRoofType() {
    return roofType;
  }

  public void setRoofType(String roofType) {
    this.roofType = roofType;
  }

  public String toString() {
    return new String("\nConstructing House \n FloorType: " + floorType
        + "\n WallType:  " + wallType + "\n RoofType:  " + roofType );
  }
}
Enter fullscreen mode Exit fullscreen mode

2 Define the Builder interface (or abstract class) along with Concrete Builders. The Builder interface contains methods for the step by step construction of the product. It also has a build method for retrieving the product object.

package com.gaurav.builder;

public interface HouseBuilder {

  public HouseBuilder buildFloor();
  public HouseBuilder buildWall();
  public HouseBuilder buildRoof();
  public House build();

}
Enter fullscreen mode Exit fullscreen mode

3 Concrete Builders implement the Builder interface. A Concrete Builder is responsible for creating and assembling a Product object. Different Concrete Builders create and assemble Product objects differently.

package com.gaurav.builder;

public class ConcreteHouseBuilder implements HouseBuilder {

  private House house;

  public ConcreteHouseBuilder() {
    house = new House();
  }

  public HouseBuilder buildFloor() {
    house.setFloorType("concrete");
    return this;
  }

  public HouseBuilder buildWall() {
    house.setWallType("concrete");
    return this;
  }

  public HouseBuilder buildRoof() {
    house.setRoofType("concrete");
    return this;
  }

  public House build() {
    return house;
  }

}
Enter fullscreen mode Exit fullscreen mode

package com.gaurav.builder;

public class WoodenHouseBuilder implements HouseBuilder {

  private House house;

  public WoodenHouseBuilder() {
    house = new House();
  }

  public HouseBuilder buildFloor() {
    house.setFloorType("wood");
    return this;
  }

  public HouseBuilder buildWall() {
    house.setWallType("wood");
    return this;
  }

  public HouseBuilder buildRoof() {
    house.setRoofType("wood");
    return this;
  }

  public House build() {
    return house;
  }

}
Enter fullscreen mode Exit fullscreen mode

4 A Director object is responsible for constructing a Product. It does this via the Builder interface to a Concrete Builder. It constructs a Product via the various Builder methods. The director class ensures that all the required operations are performed before the object is returned to the client in a 'consistent' state.

package com.gaurav.builder;

public class HouseBuildDirector {
  private HouseBuilder builder;

  public HouseBuildDirector(final HouseBuilder builder) {
    this.builder = builder;
  }

  public House construct() {
    /* call the necessary methods and return the consistent object*/
    return builder.buildFloor().buildWall().buildRoof().build();
  }

}
Enter fullscreen mode Exit fullscreen mode

5 The client code. The Client uses different builder objects to create different types of products. However, the construction process is same.

package com.gaurav.client;

import com.gaurav.builder.ConcreteHouseBuilder;
import com.gaurav.builder.HouseBuildDirector;
import com.gaurav.builder.HouseBuilder;
import com.gaurav.builder.WoodenHouseBuilder;

public class BuilderClient {

  public static void main(final String[] arguments) {

    /* Construct a concrete house */
    HouseBuilder builder = new ConcreteHouseBuilder();
    HouseBuildDirector carBuildDirector = new HouseBuildDirector(builder);
    System.out.println(carBuildDirector.construct());

    /* Construct a wooden house */
    builder = new WoodenHouseBuilder();
    carBuildDirector = new HouseBuildDirector(builder);
    System.out.println(carBuildDirector.construct());
  }
}
Enter fullscreen mode Exit fullscreen mode
Output
Constructing House 
 FloorType: concrete
 WallType:  concrete
 RoofType:  concrete

Constructing House 
 FloorType: wood
 WallType:  wood
 RoofType:  wood
Enter fullscreen mode Exit fullscreen mode

Benefits

  • Construction process can be controlled by the director.
  • Useful when many operations have to be done to build an object.
  • Avoids Telescoping Constructor Pattern.

Drawbacks

Not suitable if a mutable object is required.


Real World Examples

Building a house - We need to tell the architect what all we want as part of the building. The Architect then designs and constructs the building. It will be handed over only when everything is implemented. We do not get a 'partially' built house (which is unsafe).

Java SDK Examples

java.lang.StringBuilder append()
java.lang.StringBuffer append()
java.nio.ByteBuffer put()
javax.swing.GroupLayout.Group addComponent()
java.lang.Appendable implementations


Hope you like it. Would love to hear your thoughts on this design pattern.

Discussion (0)