design patterns

Builder Design Pattern in Java

560 VIEWS

This is an article about the Builder design pattern, and how to implement it in a Java class. Before we get buried in the details, let’s talk a little about design patterns, and what they are. As software engineers, a lot of our work involves looking for patterns in a problem and then writing code to automate those patterns.

Design patterns come about similarly. Engineers look at ways to solve complex problems, and they identify patterns which efficiently solve problems. Design patterns aren’t ready-made solutions which you can copy and paste into your code, but they do demonstrate how to structure your solution in a tried and proven manner.

In this article, we’re going to explore the Builder pattern and talk about when you want to use it and why. We’ll be implementing the pattern in an example using Java, although the principles apply to any object-oriented language.

What Problem Does the Builder Pattern Solve?

It’s always better to start with a problem and then look for a solution. So let’s begin with a problem. For our example, let’s imagine we’re building a project to store student information for a school, and we need to create an object which represents a student. And because we’re following best practices, we want this object to be immutable, so instead of using Setter methods, all fields are defined by the constructor.

A student has a first and last name, an address with street, city, state, and Zip Code, and a phone number. So we build our student object and create a constructor similar to the one below.

public Student(String firstName, String lastName, String streetAddress, String city, String zipCode, String state) {

Armed with our perfectly complete Student object defined, we start to build out our application. Many lines of code later, the administration decides they want to add some additional fields. Perhaps streetAddress2, major and startYear. So we add another constructor.

public Student(String firstName, String lastName, String streetAddress, String streetAddress2, String city, String zipCode, String state, String major, int startYear) {

That’s a little unwieldy, but still manageable — except for the fact that the requests keep coming, and before you know it, you have multiple constructors and an argument list that spans multiple lines. You’ve got too many constructors, too many arguments, and you realize you’ve made a colossal mistake.

It’s time for the builder pattern!

Introducing the Builder Pattern

The Builder Pattern allows us to construct complex objects without having to code and use multiple constructors for the object. We create a builder object, which we use to set all of the properties of the object we want to create, and then we call a build method on the builder function which creates our final object using the object’s constructor.

Some added benefits to this approach are that we can add properties in any order, and we only need to include the properties we want to set. This approach also makes it very easy to add additional properties to our final object, without having to refactor all of our code. Let’s look at an example of the Builder Pattern in action.

Implementing the Builder Pattern

Let’s start with our Student class, as shown below. Note that we have a single constructor, and we have only written getter methods.

public class Student {
   private String firstName;
   private String lastName;
   private String streetAddress;
   private String streetAddress2;
   private String city;
   private String zipCode;
   private String state;
   private String major;
   private Integer startYear;

   public Student(String firstName, String lastName, String streetAddress, 
                  String streetAddress2, String city, String zipCode, 
                  String state, String major, Integer startYear) {
       this.firstName = firstName;
       this.lastName = lastName;
       this.streetAddress = streetAddress;
       this.streetAddress2 = streetAddress2;
       this.city = city;
       this.state = state;
       this.zipCode = zipCode;
       this.major = major;
       this.startYear = startYear;
   }

   public String getFirstName() {
       return firstName;
   }

   public String getLastName() {
       return lastName;
   }

//Remaining getters excluded for brevity

}

Our first step is to add the StudentBuilder as a static inner class within the Student class. The builder should be static because we don’t want to instantiate an instance of Student so we can build a Student. The StudentBuilder has the same properties as the Student class, and then a method for each property which sets that property and returns StudentBuilder class.

public static class StudentBuilder {
   private String firstName;
   private String lastName;
   private String streetAddress;
   private String streetAddress2;
   private String city;
   private String zipCode;
   private String state;
   private String major;
   private int startYear;

   StudentBuilder() {
   }

   public StudentBuilder withFirstName(String firstName) {
       this.firstName = firstName;
       return this;
   }

   public StudentBuilder withLastName(String lastName) {
       this.lastName = lastName;
       return this;
   }

   //Remaining functions excluded for brevity - Pattern remains the same.

   public Student build() {
       return new Student(firstName, lastName, streetAddress, streetAddress2, 
                          city, zipCode, state, major, startYear);
   }
}

Trying It Out

All that remains is to test out the builder to ensure that it works. The unit test below builds a Student object, setting the first and last name, and the assertion passes.

@Test
public void builder() {
   Student student = new Student.StudentBuilder()
           .withFirstName("Thornton")
           .withLastName("Melon")
           .build();
   assertEquals("Thornton", student.getFirstName());
}

This pattern makes it very easy to enhance the functionality of the Student class, without impacting any existing code. It also makes building a Student object a lot more intuitive. The order of the properties we pass to the Builder and which properties we choose to set are all taken care of with this implementation.


Mike Mackrory is a Fixate IO Contributor. By day he works as a Senior Engineer on a Quality Engineering team and by night he writes, consults on several web-based projects and runs a marginally successful eBay sticker business.


Discussion

Click on a tab to select how you'd like to leave your comment

Leave a Comment

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

Menu