Friday, June 3, 2022

Design Pattern: Builder Pattern

Chapters

Builder Pattern

Builder pattern is a design pattern that separates object creation from objects. It means that we dedicate a class to assemble every parts of an object that we wanna create instead of assembling parts directly in the class of the object. This pattern reduces the complexity of a class. Take a look at this example.
public class ClientCode{

  public static void main(String[] args){
    House myHouse =
    House.newBuilder("My House").
    installRoof("Gable").
    installWallpaper("Green").
    installTiles("Blue").
    installRooms(3).
    build();
    
    myHouse.houseInfo();
    System.out.println();
    
    myHouse = new 
    Director(
    House.newBuilder("My Another House")).
    construct();
    
    myHouse.houseInfo();
  }
}

class House{
  
  private String houseName;
  private String roof;
  private String wallpaper;
  private String tiles;
  private int rooms;
  private boolean garage;
  
  private House(String houseName){
    this.houseName = houseName;
  }
  
  public static House.HouseBuilder newBuilder(String houseName){
    return new House(houseName).new HouseBuilder();
  }
  
  public void houseInfo(){
    System.out.println("House Name: " + houseName);
    System.out.println("Roof: " + roof);
    System.out.println("Wallpaper: " + wallpaper);
    System.out.println("Tile Color: " + tiles);
    System.out.println("# of Rooms: " + rooms);
    System.out.println("Is there a garage? " + garage);
  }
  
  class HouseBuilder implements Builder{
    
    private HouseBuilder(){
      House.this.roof = "None";
      House.this.wallpaper = "None";
      House.this.tiles = "None";
      House.this.rooms = 0;
      House.this.garage = false;
    }
    
    @Override
    public House.HouseBuilder installRoof(String roof){
      House.this.roof = roof;
      return this;
    }
    
    @Override
    public House.HouseBuilder installWallpaper(String wallpaper){
      House.this.wallpaper = wallpaper;
      return this;
    }
    
    @Override
    public House.HouseBuilder installTiles(String tiles){
      House.this.tiles = tiles;
      return this;
    }
    
    @Override
    public House.HouseBuilder installRooms(int rooms){
      House.this.rooms = rooms;
      return this;
    }
    
    @Override
    public House.HouseBuilder installGarage(boolean install){
      House.this.garage = install;
      return this;
    }
    
    @Override
    public House build(){
      return House.this;
    }
    
  }
}

interface Builder{

  House.HouseBuilder installRoof(String roof);
  House.HouseBuilder installWallpaper(String wallpaper);
  House.HouseBuilder installTiles(String tiles);
  House.HouseBuilder installRooms(int rooms);
  House.HouseBuilder installGarage(boolean install);
  House build();
  
}

class Director{
  private Builder builder; 
  
  public Director(Builder builder){
    this.builder = builder;
  }
  
  public House construct(){
    House houseInstance = null;
    
    if(builder instanceof House.HouseBuilder){
      houseInstance = builder.
      installRoof("Dutch").
      installWallpaper("Blue").
      installRooms(4).
      installGarage(true).
      build();
    }
    return houseInstance;
  }
}

Result
House Name: My House
Roof: Gable
Wallpaper: Green
Tile color: Blue
# of Rooms: 3
Is there a garage? false

House Name: My Another House
Roof: Dutch
Wallpaper: Blue
# of Rooms: 4
Is there a garage? true
You may suggest that there are alternatives to this pattern. Let's assume you're suggesting to use a constructor:

... House(String roof, String wallpaper, String tiles, int rooms, boolean garage)
...


This alternative does make sense in the example above. However, what if want two types of builds of our house? also what if each part of a house in every build has different calculations. In this case, builder pattern is more preferrable. For example, we wanna calculate the size of a roof and each build has different ways of calculating roofs.

In this case, we can just add the calculations to their installRoof methods. Imagine coding two or more types of builds in your House. Your code can become harder to read. Using build pattern, we can encapsulate those builds and their functionalities. Making our House class more clear.

Another advantage of this pattern is that clients can build an object in step-by-step manner. Also, I didn't put any getter or setter methods in the example above but we can put those methods in the House class if we want to.

You might have noticed the Director class. This class contains pre-configured builds of our builders. If you have a build that you often use, you can add that builds in this class, so that you don't need to write your build code everytime you wanna use it. We can also use this class to save client's builds.

In java, some classes implement this pattern. HttpRequest.Builder and DateTimeFormatterBuilder are some examples of classes that implement builder pattern.

No comments:

Post a Comment