Builder Design Pattern

By Hikmet Cakir

Builder Design Pattern

Builder Design Pattern is a creational design pattern. Builder pattern builds a complex object using simple objects and using a step by step approach.


For example, we have three instance variables in our class then we create a constructor for initialization. What if we have to add two more instance variable to the class after in a while? We can easily do overloading to the constructor or we can modify exist our constructor. can’t it?


Actually, this solutions aren’t very effective because if we modify exist our constructor, we have to change all initialization points. Maybe we used four times, maybe ten times, maybe over the hundred times. As number of the initialization points increase, this will be very huge problem. What if we do overloading to the our exist constructor? This solution isn’t very effective, either. Because as class’s constructor number will be increase timely and after a while it will be headache.


Additionally, we can use setter methods but what if our classes are immutable objects? This solution isn’t effective, either. In fact, we can use Builder Design Pattern to solve this kind of problem.


Implementation

I will explain three common implementations of this design pattern.


  1. Classic Implementation
  2. Separated Classic Implementation
  3. Real Life Implementation

Classic Implementation

We can do classic implementation with following steps.


  1. Create a static inner class inside the class that you want to create with builder. This static inner class is used for build to our class.
  2. Create a private constructor that take to static inner builder class as the parameter in class that you want to create.
  3. Define setter methods that return own reference inside static inner class.
  4. Define a build method that return a class that you want to build in static inner class. This build method create the class that you want to build and return it.


/**
 *
 * @author Hikmet
 * @since 23-05-2022+03:00
 */
public final class Person {
    private String firstName;
    private String lastName;
    private Integer age;
    private Float height;
    private Float weight;


    private Person(Builder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.height = builder.height;
        this.weight = builder.weight;
    }


    public String getFirstName() {
        return firstName;
    }


    public String getLastName() {
        return lastName;
    }


    public Integer getAge() {
        return age;
    }


    public Float getHeight() {
        return height;
    }


    public Float getWeight() {
        return weight;
    }


    public static class Builder {
        private String firstName;
        private String lastName;
        private Integer age;
        private Float height;
        private Float weight;


        public Builder setFirstName(String firstName) {
            this.firstName = firstName;
            return this;
        }


        public Builder setLastName(String lastName) {
            this.lastName = lastName;
            return this;
        }


        public Builder setAge(Integer age) {
            this.age = age;
            return this;
        }


        public Builder setHeight(Float height) {
            this.height = height;
            return this;
        }


        public Builder setWeight(Float weight) {
            this.weight = weight;
            return this;
        }


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

Separated Classic Implementation

This implementation has a little different with Classic Implementation. Builder class are defined different class. Not inside a class so It isn’t inner class. It is defined a separate class. Actually, this approach is less effective than Classic implementation but It is useful for some edge cases.

/**
 * It's used for definition to Person
 *
 * @author Hikmet
 * @since 26-05-2022+03:00
 */
public class Person {


    private String firstName;


    private String lastName;


    private String nationality;


    public Person(String firstName, String lastName, String nationality) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.nationality = nationality;
    }


    public String getFirstName() {
        return firstName;
    }


    public String getLastName() {
        return lastName;
    }


    public String getNationality() {
        return nationality;
    }
}



/**
 * It's used for build Person
 *
 * @author Hikmet
 * @since 26-05-2022+03:00
 */
public class PersonBuilder {


    private String firstName;


    private String lastName;


    private String nationality;


    public PersonBuilder setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }


    public PersonBuilder setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }


    public PersonBuilder setNationality(String nationality) {
        this.nationality = nationality;
        return this;
    }


    public Person build() {
        return new Person(firstName, lastName, nationality);
    }
}

Real Life Implementation

This structure is more complicate than the other structures but It’s very useful for many cases. We can create different builder classes in this structure and we’re setting value of class we want to create inside builder class.


I thought a scenario for explain this structure. There is an astrophysicist and this astrophysicist want to do various experiments about solar system in a computer simulation so astrophysicist needs to use various planets’ properties to use in her experiments.


For example, she tries to calculate some informations about Mars in a computer simulation then she will pass to another experiment and may needs to use Mars’s properties again. Because maybe astrophysicist will change many thing planet’s some properties and for the new experiment, she wants to start with fresh values about Mars. So this process will continue until she completes her the experiments. Well, let’s do it.

/**
 * It's used for representing to any planet
 *
 * @author Hikmet
 * @since 25-05-2022+03:00
 */
public class Planet {


    private Float gravity;


    private Float radius;


    private Integer orbitalPeriod;


    public void setGravity(Float gravity) {
        this.gravity = gravity;
    }


    public void setRadius(Float radius) {
        this.radius = radius;
    }


    public void setOrbitalPeriod(Integer orbitalPeriod) {
        this.orbitalPeriod = orbitalPeriod;
    }


    @Override
    public String toString() {
        return "Planet:[gravity=" + gravity + ", radius=" + radius + ", orbitalPeriod=" + orbitalPeriod + ']';
    }
}

Our planet class contains three instance variable and these variables’ setter methods. Additionally, I added a “toString()” method for test process. Well, let’s define our planet builder class.

/**
 * It's used for building to any planet
 *
 * @author Hikmet
 * @since 25-05-2022+03:00
 */
public abstract class PlanetBuilder {
    protected Planet planet;


    public Planet getPlanet() {
        return planet;
    }


    public void createPlanet() {
        planet = new Planet();
    }


    public abstract void setGravity();


    public abstract void setRadius();


    public abstract void setOrbitalPeriod();
}

This class is used for builder class to create. For example MercuryPlanetBuilder, SaturnPlanetBuilder, UranusPlanetBuilder etc. In this scenario, I will create two builder class (NeptunePlanetBuilder and MarsPlanetBuilder). Now, let’s create our builder classes.


/**
 * It's used for building to Mars planet
 *
 * @author Hikmet
 * @since 25-05-2022+03:00
 */
public class MarsPlanetBuilder extends PlanetBuilder {


    // It is Planet's Gravity, Its value calculates by m/s^2
    @Override
    public void setGravity() {
        this.planet.setGravity(3.721F);
    }


    // It is Planet's Radius, Its value calculates by km
    @Override
    public void setRadius() {
        this.planet.setRadius(3389.5F);
    }


    // It is Planet's Orbital Period, Its value calculates by days
    @Override
    public void setOrbitalPeriod() {
        this.planet.setOrbitalPeriod(687);
    }
}

I set the variables by Mars. If you look carefully, you realise that I used parent class’s instance variable for set values.


/**
 * It's used for building to Neptune planet
 *
 * @author Hikmet
 * @since 25-05-2022+03:00
 */
public class NeptunePlanetBuilder extends PlanetBuilder {


    @Override
    public void setGravity() {
        this.planet.setGravity(11.15F);
    }


    @Override
    public void setRadius() {
        this.planet.setRadius(24622F);
    }


    @Override
    public void setOrbitalPeriod() {
        this.planet.setOrbitalPeriod(165);
    }
}

Let’s check out what we did. We created abstract class named PlanetBuilder, and created two class that extend from builder class. As I was saying a astrophysicist needs to construct a planet for her experiments. Let’s define Astrophysicist class.


/**
 * This structure shows person who will analyze some planet
 *
 * @author Hikmet
 * @since 25-05-2022+03:00
 */
public class Astrophysicist {


    private PlanetBuilder planetBuilder;


    public void setPlanetBuilder(PlanetBuilder planetBuilder) {
        this.planetBuilder = planetBuilder;
    }


    public Planet getPlanet() {
        return planetBuilder.getPlanet();
    }


    public void constructPlanet() {
        planetBuilder.createPlanet();
        planetBuilder.setGravity();
        planetBuilder.setRadius();
        planetBuilder.setOrbitalPeriod();
    }
}

The astrophysicist takes the builder class in a setter method and construct the class that you want to construct class through “constructPlanet()” method. If you want to take class’s instance, you can use getPlanet() method. Additionally, you can check out all creation steps in the following test method.


import static org.junit.Assert.assertEquals;


import org.junit.Test;


public class PlanetBuilderTest {


    public static final Float MARS_GRAVITY = 3.721F;


    public static final Float MARS_RADIUS = 3389.5F;


    public static final Integer MARS_ORBITAL_PERIOD = 687;


    @Test
    public void when_constructMarsPlanetWithBuilder_expect_successCreation() {
        // Given
        Astrophysicist astrophysicist4AnalyzeMars = new Astrophysicist();
        PlanetBuilder marsBuilder = new MarsPlanetBuilder();
        astrophysicist4AnalyzeMars.setPlanetBuilder(marsBuilder);


        // When
        astrophysicist4AnalyzeMars.constructPlanet();
        Planet result = astrophysicist4AnalyzeMars.getPlanet();


        // Then
        String expected = "Planet:[" +
                "gravity=" + MARS_GRAVITY +
                ", radius=" + MARS_RADIUS +
                ", orbitalPeriod=" + MARS_ORBITAL_PERIOD +
                ']';
        String actual = result.toString();
        assertEquals(expected, actual);
    }
}

I shared the codes I explained and more in GitHub. Additionally, certainly I suggest that you should practice with this structures. I coded these structures and these structures’ some test scenarios. You can check out to repository.

I used various resource for prepare this essay. I indicated in following. You can check out.