The intention is to abstract steps of construction of objects when there are lot of parameters and to provide thread safety at the time of object creation. You will have Class and ClassBuilder(static inner class) which has same parameters as Class, setters for parameters and a build method. The setter methods in ClassBuilder helps to carry validation over parameters before the object for Class gets created in build method

Why builder Pattern?
If there are many attributes in the class and object is created over several calls it may be in an inconsistent state partway through its construction. To address this builder pattern is introduced. If Object Creation should be delayed until all the validation of class attributes are performed builder pattern addresses the need.

If there are 2 attributes to a Class then we can create 2^N Constructor based on the two attributes. This Results in Boiler plate code. What if the number of attributes keeps increasing over a period of time.

Report.java

import java.util.InvalidPropertiesFormatException;

public class Report {
    private String reportName;
    private String reportFormat;

    private Report(){}

    public static ReportBuilder getReportBuilder(){
        return new ReportBuilder();
    }


    @Override
    public String toString() {
        return "Report{" +
                "reportName='" + reportName + '\'' +
                ", reportFormat='" + reportFormat + '\'' +
                '}';
    }

    public static class ReportBuilder {
        private String reportName;
        private String reportFormat;

        private ReportBuilder(){}

        public ReportBuilder setReportName(String reportName) {
            this.reportName = reportName;
            return this;
        }

        public ReportBuilder setReportFormat(String reportFormat) {
            this.reportFormat = reportFormat;
            return this;
        }

        public Report build() throws InvalidPropertiesFormatException {
            if(this.reportFormat == "CSV"){
                throw new InvalidPropertiesFormatException("Format Not Supported");
            }

            if(this.reportName.length() < 10){
                throw new InvalidPropertiesFormatException("Name should be greater than 10");
            }

            //Object for Report should not be created before validation is completed as above
            Report objReport = new Report();
            objReport.reportName = this.reportName;
            objReport.reportFormat = this.reportFormat;
            return objReport;
        }
    }
}

BuilderPatternClient.java

import java.util.InvalidPropertiesFormatException;

public class BuilderPatternClient {
    public static void main(String[] args) throws InvalidPropertiesFormatException {
        Report objReport = Report.getReportBuilder()
                .setReportFormat("PDF")
                .setReportName("Monthly Transactions")
                .build();

        System.out.println(objReport);
    }
}

Output

Report{reportName='Monthly Transactions', reportFormat='PDF'}

What is Telescoping Pattern
A telescoping constructor is a series of constructors where each one has a different number of parameters. Each constructor calls a more specific constructor in the hierarchy, providing a default value for the remaining parameters.

Disadvantage of Below Approach

  1. Constructors are hard to read when they have many parameters
  2. When adding new parameters, you have to add new constructors to the chain. This pattern can therefore not scale very well
  3. It becomes difficult to remember the required order of the parameters, especially when many of them are of the same type. Additionally, the code becomes less readable, and it’s not always clear what is being passed to the constructor. This is addressed by using Builder Pattern

Report

import java.util.Date;

public class Report {
    private String reportName;
    private Integer reportSize;
    private String  reportFormat;
    private Date reportDate;

    public Report() {
    }

    //Constructor1
    public Report(String reportName) {
        this.reportName = reportName;
    }

    public Report(String reportName, Integer reportSize) {
        this(reportName); //Call to before Constructor1
        this.reportSize = reportSize;
    }

    public Report(String reportName, Integer reportSize, String  reportFormat) {
        this(reportName, reportSize); //Call to before Constructor2
        this.reportFormat = reportFormat;
    }

    public Report(String reportName, Integer reportSize, String  reportFormat, Date reportDate) {
        this(reportName, reportSize, reportFormat); //Call to before Constructor3
        this.reportDate = reportDate;
    }
}