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
- If you want to upskill your Java, you should definitely check out this bestseller course
Follow me on Medium & LinkedIn
More blogs @https://asyncq.com/