Builder Pattern

Builder Pattern

Underlying Problem

So, you learned about OOP like, objects, classes, constructors and so on. Now you can't wait to make use of them. You started implementing your class, everything seemed fine until you came to write constructor part. You have noticed smelly code in your constructor. It's either requiring you to create a constructor with many parameters or multiple different constructors. You are on the way to create a wordy, not scalable and hard to read design.

Consider the following example :

import java.time.LocalDate;

public class Book {
    private String author;
    private String title;
    private String publisher;
    private String language;
    private String subject;
    private String iSBN;
    private String format;
    private LocalDate publishDate;
    private String edition;
    private Integer pages;

    public Book(String author, String title, String publisher,
                String language, String subject, String iSBN,
                String format, LocalDate publishDate,
                String edition, Integer pages) {
        this.author = author;
        this.title = title;
        this.publisher = publisher;
        this.language = language;
        this.subject = subject;
        this.iSBN = iSBN;
        this.format = format;
        this.publishDate = publishDate;
        this.edition = edition;
        this.pages = pages;
    }
}

Just to create an object of Book class you would have to provide all the parameters. However, you may need an object only with title and author. Another client may want to create with author, title and ISBN and so on. Imagine just providing above constructor to the clients, you would be cursed for your above design. See sample below (Warning! Disturbing Content! Viewer discretion is advised!)

Book myBook = new Book("Walter Savitch", "Absolute Java",
                "Pearson", "English", "Programming", "9780134041674", "Paper", 
                LocalDate.of(2013, 2, 20), 
                "5th edition", 776);

One possible solution for this would be creating multiple constructors with different number of parameters.

import java.time.LocalDate;

public class Book {
    private String author;
    private String title;
    private String publisher;
    private String language;
    private String subject;
    private String iSBN;
    private String format;
    private LocalDate publishDate;
    private String edition;
    private Integer pages;

    public Book(String author, String title, String publisher,
                String language, String subject, String iSBN,
                String format, LocalDate publishDate,
                String edition, Integer pages) {
        this.author = author;
        this.title = title;
        this.publisher = publisher;
        this.language = language;
        this.subject = subject;
        this.iSBN = iSBN;
        this.format = format;
        this.publishDate = publishDate;                
        this.edition = edition;
        this.pages = pages;
    }

    public Book(String author, String title) {
        this.author = author;
        this.title = title;
        this.publisher = publisher;
        this.language = language;
    }

    /*
        And a bunch of other constructors
    */

}

But that's very long long lines of code. Well, you may remember creating an object with an empty constructor and assign values using setters like myBook.setTitle("The title"); myBook.setAuthor("The author");. There is another problem with it, you may want to have an immutable class and the setters clearly violate immutability rule. So what is the solution then? Now we've come to the point.

Solution - Builder Pattern

The Builder Pattern is a design pattern designed to provide a flexible solution to various object creation problems in object-oriented programming. Instead of making the desired the object directly, the client calls a constructor(or a static factory) with all the required parameters and gets a builder object. Then the client calls setter-like methods on the builder object to set each optional parameter of interest. Finally, the client calls a parameterless build method to generate the object, which is typically immutable. Here's how we design it in practice in the example of our Book class


import java.time.LocalDate;

public class Book {
    private String author;
    private String title;
    private String publisher;
    private String language;
    private String subject;
    private String iSBN;
    private String format;
    private LocalDate publishDate;
    private String edition;
    private Integer pages;

    public static class Builder {
        private String author;
        private String title;
        private String publisher;
        private String language;
        private String subject;
        private String iSBN;
        private String format;
        private LocalDate publishDate;
        private String edition;
        private Integer pages;

        public Builder(String author, String title) {
            this.author = author;
            this.title = title;
        }

        public Builder publisherPublishedAt(String publisher, LocalDate publishDate) {
            this.publisher = publisher;
            this.publishDate = publishDate;
            return this;
        }

        public Builder languageAndSubject(String language, String subject) {
            this.language = language;
            this.subject = subject;
            return this;
        }

        public Builder iSBNAndFormat(String iSBN, String format) {
            this.iSBN = iSBN;
            this.format = format;
            return this;
        }
        public Builder editionAndPages(String edition, Integer pages) {
            this.edition = edition;
            this.pages = pages;
            return this;
        }

        public Book build() {
            return new Book(this);
        }
    }

    private Book(Builder builder) {
        author = builder.author;
        title = builder.title;
        publisher = builder.publisher;
        language = builder.language;
        subject = builder.subject;
        iSBN = builder.iSBN;
        format = builder.format;
        publishDate = builder.publishDate;
        edition = builder.edition;
        pages = builder.pages;
    }
}

Now, with above design the client(whoever uses Book class) will be praising you not cursing! Look at three examples below to see how easy to read and write code with a builder pattern.

//With two parameters
Book bookWithAuthorAndTitle = new Book.Builder("Walter Savitch", "Absolute Java").build();

//Scale the object to have four parameters
Book bookWithLanguageAndSubject = new Book.Builder("Walter Savitch", "Absolute Java")
                .languageAndSubject("English", "Programming").build();

 //Scale it again, easily
Book bookAndSomeDetails = new Book.Builder("Walter Savitch", "Absolute Java")
                .languageAndSubject("English", "Programming")
                .iSBNAndFormat("9780134041674", "Paper").build();

Conclusion

In summary, the Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters, especially if many of the parameters are optional or of identical type. Client code is much easier to read and write with builders.

Photo by William Wendling on Unsplash