Builder Design Pattern

Builder Design Pattern

ยท

4 min read

Builder design pattern is a creational design pattern.

Problems can be solved by builder design pattern.

If a class has many fields and creates an object using too many arguments by the client.


public class Employee {

    private String id;
    private String name;
    private String address;

    public Employee(String id) {
        this.id = id;
    }

    public Employee(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public Employee(String id, String name, String address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
}

Object creation

Create an employee object by id

Employee employee = new Employee("2210dshaj");
or 
Employee employee = new Employee("2210dshaj", null, null);

Create an object by name

You can't create multiple constructors with the same argument data type like

 public Employee(String id) {
     this.id = id;
 } 

 Compile-time error:  'Employee(String)' is already defined
 public Employee (String name){
     this.id=id;
 } 

 public Employee(String id, String name) {
      this.id = id;
      this.name = name;
 }

 Compile-time error: 'Employee(String, String)' is already defined
 public Employee(String name, String address) {
      this.name = name;
      this.address = address;
 }

employee object creation.

Employee employee = new Employee(null, "Mukul" , null);

In the above example there are only three fields, let's assume if the employee has more fields then creating an object based on a specific field value is not recommended using the constructor.

There are many problems while creating an object using a constructor.

  1. Many Constructor, More code

  2. Passing null while creating an object.

  3. Less readable code.

These problems can be solved by builder design patterns

Let's take a look at builder design patterns implementation.


Steps:

  1. Create a static inner class with the same field as the outer class.

  2. Create setter methods for each field that should return an inner builder class instance.

  3. Create a method that should return an outer class instance.

  4. The outer class constructor should be private and with an inner class object as an argument.

  5. Outer class only has getters, not setters

public class Response<T>{
    private int statusCode;
    private int status;
    private String message;
    private T dto;
    private Map<String,Object> additionalData;

    private Response(ResponseBuilder<T> builder){
        this.statusCode=builder.statusCode;
        this.status=builder.status;
        this.message=builder.message;
        this.dto=builder.dto;
        this.additionalData=builder.additionalData;
    }

    public int getStatusCode() {
        return statusCode;
    }

    public int getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }

    public T getDto() {
        return dto;
    }

    public Map<String, Object> getAdditionalData() {
        return additionalData != null ? additionalData : new HashMap<String,Object>();
    }

    public static <T> ResponseBuilder<T> builder() {
        return new ResponseBuilder<T>();
    }

    public static class ResponseBuilder<T>{
        private int statusCode;
        private int status;
        private String message;
        private T dto;
        private Map<String,Object> additionalData;

        public ResponseBuilder<T> statusCode(int statusCode){
            this.statusCode=statusCode;
            return this;
        }

        public ResponseBuilder<T> status(int status){
            this.status=status;
            return this;
        }

        public ResponseBuilder<T> message(String message){
            this.message=message;
            return this;
        }

        public ResponseBuilder<T> entity(T dto){
            this.dto=dto;
            return this;
        }

        public ResponseBuilder<T> additionalData(Map<String,Object> additionalData){
            this.additionalData=additionalData;
            return this;
        }

        public Response<T> build(){
            return new Response<T>(this);
        }
    }
}

How do we create objects using a builder design pattern

 Response<Employee> employeeResponseBuilder = Response.<Employee> builder()
                .status(200)
                .entity(new Employee("1234", "Muk"))
                .message("Created")
                .build();

Real-world use

StringBuilder uses a builder design.

    @Override
    @IntrinsicCandidate
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

Amazon S3 Connections:

 @Bean
    public AmazonS3 getS3Client() {
        BasicAWSCredentials awsCreds = new BasicAWSCredentials("access_key"
              , "secret_key");

        // Builder design patterns    
        return AmazonS3ClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(awsCreds))
                .withRegion(Regions.fromName("your-bucket-region"))
                .build();
    }

Google OAuth 2.0 service uses Builder design patterns to avoid adding more arguments to the constructor and for immutability purposes.

GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow

 .Builder(HTTP_TRANSPORT, JSON_FACTORY,clientSecrets, SCOPES)
 .setDataStoreFactory(
  new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH))
)     
.setAccessType("offline")
.build();

Google Drive service

Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
        .setApplicationName(APPLICATION_NAME)
        .build();
FileList result = service.files().list()
                .setPageSize(10)
                .setFields("nextPageToken, files(id, name)")
                .execute();

Hibernate CriteriaQueryBuilder

 CriteriaQuery query = new CriteriaQueryBuilder(session, Employee.class)
            .addEqual("department", "IT")
            .addLike("name", "John%")
            .build();

Builder design pattern using Lombok

You can also leverage the Lombok feature by using builder annotation.

https://projectlombok.org/features/Builder

@Builder
@Getter
public class Response <T> {
    private int statusCode;
    private int status;
    private String message;
    private T dto;
    private Map<String,Object> additionalData;
}

Hope you enjoyed reading! Your support means a lot.

Feel free to like and share if you find it valuable. Thanks for your time! ๐Ÿ™

Did you find this article valuable?

Support MUKUL JHA by becoming a sponsor. Any amount is appreciated!

ย