builder design pattern in java for beginners feature image

 Mastering the Builder Design Pattern in Java: Simplifying Object Creation

Understanding the Builder design pattern in Java is crucial for efficient and flexible object creation. The Builder pattern offers a structured approach to constructing complex objects while promoting readability and maintainability.

In this blog post, we will explore the Builder design pattern, its benefits, and its components to help you grasp the concept effectively.

To better understand Design Patterns in Java, including its categories and benefits, please refer to “Design Patterns in Java.”


What is the Builder Design Pattern?

The Builder design pattern in Java separates the construction of complex objects from their representation. It provides an intuitive API for creating objects step by step.

This pattern is widely used in software development and offers advantages over creational patterns like Factory and Singleton.


Benefits of Using the Builder Pattern

  1. Flexibility: The Builder pattern allows for flexible object construction by providing methods to set specific attributes based on individual needs.
  2. Readability: By separating the construction process from the object representation, the Builder pattern enhances code readability and maintainability.
  3. Reusability: The same construction process can be used to create different representations of the object, promoting code reusability
  4. Comparison with Other Creational Patterns: The Builder pattern offers advantages like increased flexibility over the Factory pattern and improved object construction control compared to the Singleton pattern.

Components of the Builder Design Pattern

  • Director: The Director class controls the construction process of the object. It interacts with the Builder interface and guides the order and sequence of steps required to build the object.
  • Builder: The Builder interface defines methods for constructing the product object. It declares abstract methods for setting attributes involved in building the object, providing flexibility for different configurations.
  • Concrete Builder: The Concrete Builder class implements the Builder interface. It provides specific implementations for each method, encapsulating the construction logic and holding the partially built object.
  • Product: The Product class represents the complex object being constructed. It contains attributes and properties that define the object. The Product is often the result of the construction process.
builder design pattern in java component diagram
Builder Design Pattern – Components

The interaction between these components follows a typical flow:

  • The client interacts with the Director to initiate the construction process.
  • The Director communicates with the Concrete Builder by invoking the appropriate methods defined in the Builder interface.
  • The Concrete Builder handles each step and sets the corresponding values in the Product.
  • The client can retrieve the entirely constructed Product from the Concrete Builder through a build method or similar mechanism.

Builder Design Pattern Example Implementation

To better understand the Builder pattern, consider an example of constructing a User DTO object. Then, we’ll demonstrate the implementation using code snippets.

Example: Building a User DTO Object in Java

Suppose you want to construct the User DTO Object using various features of users such as name, age, address, etc. Follow the below steps to implement the Builder Design Pattern in Java.

Step 1: Define the Product, the UserDTO

// Product - the final object needs to be created
public class UserDTO {
	private String name;
	private String birthday;
	private String address;
	private static UserDTOBuilder userDTOBuilder;

	public String getName() {
		return name;
	}

	private void setName(String name) {
		this.name = name;
	}

	public String getBirthday() {
		return birthday;
	}

	private void setBirthday(String birthday) {
		this.birthday = birthday;
	}

	public String getAddress() {
		return address;
	}

	private void setAddress(String address) {
		this.address = address;
	}

	public String toString() {
		return "UserDTO [name=" + name + ", birthday=" + birthday + ", address=" + address + "]";
	}
}

Step 2: Add Concrete Builder and Builder class as an Inner Static class to add the implementation for the UserDTO

// Get builder instance
	//create a snglton class
	public static UserDTOBuilder getBuilder() {
		if (userDTOBuilder == null) {
			userDTOBuilder = new UserDTOBuilder();
		}
		return userDTOBuilder;
	}

	// Builder and concrete Class declared as a inner static class
	public static class UserDTOBuilder {
		private String firstName;
		private String lastName;
		private String age;
		private String address;
		private UserDTO userDTO;

		public UserDTOBuilder withFirstName(String fname) {
			firstName = fname;
			// returning this in return helps in creating the method chaining
			return this;
		}

		public UserDTOBuilder withLastName(String lname) {
			lastName = lname;
			return this;
		}

		public UserDTOBuilder withBirthday(LocalDate date) {
			Period ageInyears = Period.between(date, LocalDate.now());
			age = String.valueOf(ageInyears.getYears());
			return this;
		}

		public UserDTOBuilder withAddress(Address address) {
			this.address = address.getHouseNumber() + " ," + address.getStreet() + " ," +  address.getCity() + " ,"
					+ address.getState() + " ," + address.getZipcode();
			return this;
		}

		public UserDTO build() {
			userDTO = new UserDTO();
			userDTO.setName(firstName + " " + lastName);
			userDTO.setAddress(address);
			userDTO.setBirthday(age);
			return userDTO;

		}

		public UserDTO getUserDTO() {
			return this.userDTO;
		}
	}

Step 3: Now, let’s see how we can create UserDTO objects using the Builder Pattern

public static void main(String[] args) {
		System.out.println(getUserDTO(UserDTO.getBuilder(), createUser()));
		System.out.println(UserDTO.getBuilder().getUserDTO());
	}

	/**
	 * This is acting as a director which calls the builder to get user DTO
	 * 
	 * @param builder
	 * @param user
	 * @return
	 */
	private static UserDTO getUserDTO(UserDTOBuilder builder, User user) {
		return builder.withFirstName(user.getFirstName()).withLastName(user.getLastName())
				.withAddress(user.getAddress()).withBirthday(user.getBirthday()).build();
	}

	/**
	 * Returns a sample user.
	 */
	public static User createUser() {
		User user = new User();
		user.setBirthday(LocalDate.of(1960, 5, 6));
		user.setFirstName("Ron");
		user.setLastName("Swanson");
		Address address = new Address();
		address.setHouseNumber("100");
		address.setStreet("State Street");
		address.setCity("Pawnee");
		address.setState("Indiana");
		address.setZipcode("47998");
		user.setAddress(address);
		return user;
	}

Conclusion

The Builder design pattern simplifies the creation of complex objects by separating the construction process from the object representation. By following a step-by-step configuration and leveraging a straightforward API, the Builder pattern enhances code readability and reusability.

So, the next time you face the challenge of constructing complex objects with various configurations, embrace the power of the Builder pattern in Java. It will make your code more manageable, maintainable, and flexible. Happy building!

Remember, the provided example’s source code is available on GitHub.

AWS Community Builder | Software Architect @ Gigaforce | Serverless | Aspiring Entrepreneur | Quirky Introvert­čśé

Leave a Reply

Your email address will not be published. Required fields are marked *