Builder Pattern In Java Explained!

  • Post last modified:December 15, 2022
  • Reading time:4 mins read
Photo by Marcel Strauß on Unsplash

Introduction

  • In this article we will cover about Builder design pattern which helps us building complex objects.
  • Builder design pattern is a creational design pattern type that focuses on creating an Instance.
  • Lets quickly visit an example that explains about creating Instances with Builder pattern

Use Case

  • Consider a case where we have Message POJO that needs to be published to Message queue.
  • Typically these message has mandatory data , some optional data / metadata etc.
  • For example in some cases token and clientId might be empty while in some cases these will be required.
  • Then how can design object creation project such that we can make a certain field mandatory while others and optional.

Creating Constructor

  • One of the ways to achieve object creation is by creating different parametarized constructors.
  • But soon we will realize that this is not scalable when we have too many fields in the class.

Initialize with Setters

  • Another way is to initialize the properties with setters. 
  • This approach helps to create object but the object is mutable which means it can be changed through setters after initial construction.

Builder Pattern

  • Builder pattern takes some inspiration from fluent design patterns and includes immutability at their core in object creation.
  • At first, we protect object creation through the constructor by making the constructor Private.
public class Message {

    private String id;
    private String message;
    private Date date;
    private String token;
    private String clientId;

    private Message(MessageBuilder builder){
        this.id = builder.id;
        this.clientId = builder.clientId;
        this.message = builder.message;
        this.token = builder.token;
        this.date = builder.date;
    }

    public String getId() {
        return id;
    }

    public String getMessage() {
        return message;
    }

    public Date getDate() {
        return date;
    }

    public String getToken() {
        return token;
    }

    public String getClientId() {
        return clientId;
    }

    @Override
    public String toString() {
        return "Message{" +
                "id='" + id + '\'' +
                ", message='" + message + '\'' +
                ", date=" + date +
                ", token='" + token + '\'' +
                ", clientId='" + clientId + '\'' +
                '}';
    }
  }
  • Now since we have restricted object creation through the constructor, we will create builder class that will take the job to create object.
  • We create set method for all the fields present in Message class in builder class. We give fluent name so that builder logic makes it fluent when we create the object.
  • Once we have set up the properties , we call the build method to construct instance of Message type.
public static class MessageBuilder{
        private String id;
        private String message;
        private Date date;
        private String token;
        private String clientId;

        public MessageBuilder withMessageId(String messageId){
            this.id = messageId;
            return this;
        }

        public MessageBuilder withMessage(String message){
            this.message = message;
            return this;
        }

        public MessageBuilder withPublishedDate(Date date){
            this.date = date;
            return this;
        }

        public MessageBuilder withClientId(String clientId){
            this.clientId= clientId;
            return this;
        }

        public MessageBuilder withClientToken(String clientToken){
            this.token = token;
            return this;
        }

        public Message build(){
            return new Message(this);
        }
    }
  • Once the build method is called it returns the initialized Message Instance.
  • The returned Instance is immutable since there is not setter in Message class.

Client Code

  • Now our client code helps us initializing and build the Message Instance.
public class Client {


    public static void main(String[] args) {
        Message message = new MessageBuilder()
                .withMessageId(UUID.randomUUID().toString())
                .withMessage("Test Message")
                .withPublishedDate(new Date())
                .build();

        System.out.println(message.toString());
        
    }
 }
  • In above code, we are not setting token or client code hence these properties will be null
  • But if we need to setup these property in some cases then we can easily do so.
Message message1 = new MessageBuilder()
                .withMessageId(UUID.randomUUID().toString())
                .withMessage("Test Message")
                .withPublishedDate(new Date())
                .withClientToken("token")
                .withClientId("client id")
                .build();

        System.out.println(message1.toString());

Conclusion

  • In this article, we learn about builder design pattern with the example of message publisher.
  • Builder design patterns help us create complex objects in a much more efficient and fluent manner.
  • It provides us immutable object which is not changeable by further in the program hence reducing side effects.

Bonus Tip

Follow me on Medium & LinkedIn
More blogs @https://asyncq.com/

Leave a Reply