Guided builder pattern

Previously, I wrote a post about builder pattern. This post intends to extend the builder pattern further. Let’s start with previous working example of Employee class as follows.

import java.util.Date;

public class Employee {
    private final int id;               //required
    private final String name;          //required
    private final String address;       //optional
    private final String phoneNumber;   //optional
    private final Date dateOfBirth;     //optional
    private final String placeOfBirth;  //optional
    private final String ssn;           //optional

    private Employee(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.address = builder.address;
        this.phoneNumber = builder.phoneNumber;
        this.dateOfBirth = builder.dateOfBirth;
        this.placeOfBirth = builder.placeOfBirth;
        this.ssn = builder.ssn;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public String getPlaceOfBirth() {
        return placeOfBirth;
    }

    public String getSsn() {
        return ssn;
    }

    public Builder unbuild() {
        return new Builder(this);
    }

    public static class Builder {
        private final int id;
        private final String name;
        private String address;
        private String phoneNumber;
        private Date dateOfBirth;
        private String placeOfBirth;
        private String ssn;

        public Builder(int id, String name) {
            this.id = id;
            this.name = name;
        }

        private Builder(Employee employee) {
            this.id = employee.getId();
            this.name = employee.getName();
            this.address = employee.getAddress();
            this.phoneNumber = employee.getPhoneNumber();
            this.dateOfBirth = employee.getDateOfBirth();
            this.placeOfBirth = employee.getPlaceOfBirth();
            this.ssn = employee.getSsn();
        }

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

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

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

        public Builder dateOfBirth(Date dateOfBirth) {
            this.dateOfBirth = dateOfBirth;
            return this;
        }

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

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

So what’s the problem with this implementation? If we want the users of this class to actually initialize all field values properly, then we can’t enforce the users to do so since users can just call the builder to instantiate an object of Employee as follows.

Employee employee = Employee.builder(1, "Watson")
        .address("Baker street, London")
        .phoneNumber("+1234567890")
        .build();

employee object has only id, name, address, and phoneNumber filled, and it is a valid call. The question would be, how to prevent such call being made? Guided builder pattern gives you the answer.

Guided builder pattern is intended to guide the users of the class in instantiating an object. This can be done by, first, making the static Builder class to have 1) a default constructor (without parameter), 2) constructor that accept Employee as parameter, and then remove final from both id and name field.

private int id;
private String name;
private String address;
private String phoneNumber;
private Date dateOfBirth;
private String placeOfBirth;
private String ssn;

private Builder() {}

private Builder(Employee employee) {
 this.id = employee.getId();
 this.name = employee.getName();
 this.address = employee.getAddress();
 this.phoneNumber = employee.getPhoneNumber();
 this.dateOfBirth = employee.getDateOfBirth();
 this.placeOfBirth = employee.getPlaceOfBirth();
 this.ssn = employee.getSsn();
}

Second, we create interfaces which each has a field assignment method. The return type of the field assignment method in each interface is another interface that assigns other field. This is done until all interfaces to assign fields are implemented. The last interface returns the static Builder class.

public interface IdBuilder {
    NameBuilder id(int id);
}

public interface NameBuilder {
    AddressBuilder name(String name);
}

public interface AddressBuilder {
    PhoneNumberBuilder address(String address);
}

public interface PhoneNumberBuilder {
    DateOfBirthBuilder phoneNumber(String phoneNumber);
}

public interface DateOfBirthBuilder {
    PlaceOfBirthBuilder dateOfBirth(Date dateOfBirth);
}

public interface PlaceOfBirthBuilder {
    SsnBuilder placeOfBirth(String placeOfBirth);
}

public interface SsnBuilder {
    Builder ssn(String ssn);
}

Third, the builder and unbuild methods in the class need to return the first interface type.

public static IdBuilder builder() {
    return new Builder();
}

public IdBuilder unbuild() {
    return new Builder(this);
}

Last, the static Builder class needs to implement those interfaces.

public static class Builder implements IdBuilder, NameBuilder, 
        AddressBuilder, PhoneNumberBuilder, DateOfBirthBuilder, 
        PlaceOfBirthBuilder, SsnBuilder{
    private int id;
    private String name;
    private String address;
    private String phoneNumber;
    private Date dateOfBirth;
    private String placeOfBirth;
    private String ssn;

    private Builder() {}

    private Builder(Employee employee) {
        this.id = employee.getId();
        this.name = employee.getName();
        this.address = employee.getAddress();
        this.phoneNumber = employee.getPhoneNumber();
        this.dateOfBirth = employee.getDateOfBirth();
        this.placeOfBirth = employee.getPlaceOfBirth();
        this.ssn = employee.getSsn();
    }

    public NameBuilder id(int id) {
        this.id = id;
        return this;
    }

    public AddressBuilder name(String name) {
        this.name = name;
        return this;
    }

    public PhoneNumberBuilder address(String address) {
        this.address = address;
        return this;
    }

    public DateOfBirthBuilder phoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }

    public PlaceOfBirthBuilder dateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
        return this;
    }

    public SsnBuilder placeOfBirth(String placeOfBirth) {
        this.placeOfBirth = placeOfBirth;
        return this;
    }

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

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

By doing this, it only allows the users of the class to instantiate object as follows.

Employee employee = Employee.builder()
        .id(1234)
        .name("Watson")
        .address("Baker street, London")
        .phoneNumber("+1234567890")
        .dateOfBirth(new Date())
        .placeOfBirth("12345")
        .ssn("123121323")
        .build();

Another handy thing is that we can enforce certain value NOT to be accepted by using Guava checkNotNull, for instance, within the implementation of the field assignment methods.

The complete Employee class implementation is as follows.

import java.util.Date;

public class Employee {
    private final int id;
    private final String name;
    private final String address;
    private final String phoneNumber;
    private final Date dateOfBirth;
    private final String placeOfBirth;
    private final String ssn;

    private Employee(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.address = builder.address;
        this.phoneNumber = builder.phoneNumber;
        this.dateOfBirth = builder.dateOfBirth;
        this.placeOfBirth = builder.placeOfBirth;
        this.ssn = builder.ssn;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public String getPlaceOfBirth() {
        return placeOfBirth;
    }

    public String getSsn() {
        return ssn;
    }

    public static IdBuilder builder() {
        return new Builder();
    }

    public IdBuilder unbuild() {
        return new Builder(this);
    }

    public interface IdBuilder {
        NameBuilder id(int id);
    }

    public interface NameBuilder {
        AddressBuilder name(String name);
    }

    public interface AddressBuilder {
        PhoneNumberBuilder address(String address);
    }

    public interface PhoneNumberBuilder {
        DateOfBirthBuilder phoneNumber(String phoneNumber);
    }

    public interface DateOfBirthBuilder {
        PlaceOfBirthBuilder dateOfBirth(Date dateOfBirth);
    }

    public interface PlaceOfBirthBuilder {
        SsnBuilder placeOfBirth(String placeOfBirth);
    }

    public interface SsnBuilder {
        Builder ssn(String ssn);
    }

    public static class Builder implements IdBuilder, NameBuilder,
            AddressBuilder, PhoneNumberBuilder, DateOfBirthBuilder,
            PlaceOfBirthBuilder, SsnBuilder{
        private int id;
        private String name;
        private String address;
        private String phoneNumber;
        private Date dateOfBirth;
        private String placeOfBirth;
        private String ssn;

        private Builder() {}

        private Builder(Employee employee) {
            this.id = employee.getId();
            this.name = employee.getName();
            this.address = employee.getAddress();
            this.phoneNumber = employee.getPhoneNumber();
            this.dateOfBirth = employee.getDateOfBirth();
            this.placeOfBirth = employee.getPlaceOfBirth();
            this.ssn = employee.getSsn();
        }

        public NameBuilder id(int id) {
            this.id = id;
            return this;
        }

        public AddressBuilder name(String name) {
            this.name = name;
            return this;
        }

        public PhoneNumberBuilder address(String address) {
            this.address = address;
            return this;
        }

        public DateOfBirthBuilder phoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
            return this;
        }

        public PlaceOfBirthBuilder dateOfBirth(Date dateOfBirth) {
            this.dateOfBirth = dateOfBirth;
            return this;
        }

        public SsnBuilder placeOfBirth(String placeOfBirth) {
            this.placeOfBirth = placeOfBirth;
            return this;
        }

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

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

I hope this post is useful.

Advertisements

Builder pattern

There are so many different design patterns in programming, e.g., builder pattern, guided builder pattern, factory/supplier pattern, factory-method pattern, visitor pattern, etc. Design patterns are merely guidelines for effective coding, but they have their merits in solving commonly re-occuring problems. This article explains one of design patterns called builder pattern. Before this post becomes TL;DR, let’s dive directly to codes in Java.

Let’s start with a problem statement. Suppose you have a typical Employee class with two attributes: id and name as follows.

public class Employee {
    private final int id;    //required
    private final String name;  //required
    
    public Employee(final int id, final String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

You now have business requirement to add more optional fields to the class: address, phoneNumber,  dateOfBirth, placeOfBirth, ssn. You will have many constructors shown below.

import java.util.Date;

public class Employee {
    private final int id;               //required
    private final String name;          //required
    private final String address;       //optional
    private final String phoneNumber;   //optional
    private final Date dateOfBirth;     //optional
    private final String placeOfBirth;  //optional
    private final String ssn;           //optional

    public Employee(final int id, final String name) {
        this(id, name, "", "", null, "", "");
    }

    public Employee(final int id, final String name, String address) {
        this(id, name, address, "", null, "", "");
    }

    public Employee(final int id, final String name,
                    final String address, final String phoneNumber) {
        this(id, name, address, phoneNumber, null, "", "");
    }

    public Employee(final int id, final String name,
                    final String address, final String phoneNumber,
                    final Date dateOfBirth) {
        this(id, name, address, phoneNumber, dateOfBirth, "", "");
    }

    public Employee(final int id, final String name,
                    final String address, final String phoneNumber,
                    final Date dateOfBirth, final String placeOfBirth) {
        this(id, name, address, phoneNumber, dateOfBirth, placeOfBirth, "");
    }

    public Employee(final int id, final String name, final String address,
                    final String phoneNumber, final Date dateOfBirth,
                    final String placeOfBirth, final String ssn) {
        this.id = id;
        this.name = name;
        this.address = address;
        this.phoneNumber = phoneNumber;
        this.dateOfBirth = dateOfBirth;
        this.placeOfBirth = placeOfBirth;
        this.ssn = ssn;
    }
    
    ...   
}

You can see the downside immediately. First, you cannot make another constructor which have the same signature. For instance, constructor with id, name, and phoneNumber is not possible to be created since you already have a constructor with id, name, address (i.e., int, String, String signature). Second, as the field number grows, you will have difficulty in maintaining the codes and deciding which constructors are needed. Third, it’s not clear to the users of this class which constructor they should call.

We can easily solve these problems by simply following JavaBeans convention, i.e, setting all fields to be non-final and providing setter methods for all fields. However, this introduces problem of mutable state (a big no for concurrency programming) which hard to debug. This also means any instantiated object of the class can be in inconsistent state throughout its lifetime within the client/user codes.

Fortunately, we have builder pattern comes to the rescue. Builder pattern tackles all those problems mentioned above without losing the advantages of immutable state.

import java.util.Date;

public class Employee {
    private final int id;               //required
    private final String name;          //required
    private final String address;       //optional
    private final String phoneNumber;   //optional
    private final Date dateOfBirth;     //optional
    private final String placeOfBirth;  //optional
    private final String ssn;           //optional

    private Employee(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.address = builder.address;
        this.phoneNumber = builder.phoneNumber;
        this.dateOfBirth = builder.dateOfBirth;
        this.placeOfBirth = builder.placeOfBirth;
        this.ssn = builder.ssn;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public String getPlaceOfBirth() {
        return placeOfBirth;
    }

    public String getSsn() {
        return ssn;
    }

    public static Builder builder(int id, String name) {
        return new Builder(id, name);
    }

    public static class Builder {
        private final int id;
        private final String name;
        private String address;
        private String phoneNumber;
        private Date dateOfBirth;
        private String placeOfBirth;
        private String ssn;

        private Builder(int id, String name) {
            this.id = id;
            this.name = name;
        }

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

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

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

        public Builder dateOfBirth(Date dateOfBirth) {
            this.dateOfBirth = dateOfBirth;
            return this;
        }

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

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

The constructor for Employee is private, so new object can only be instantiated by using the builder static method which return Builder. Also, the Builder class has a fluent interface to set the value of optional fields, whereas the required fields are passed as constructor argument. You can look on how readable the code for instantiating a new Employee object.

Employee employee = Employee.builder(1, "Watson")
        .address("Baker street, London")
        .phoneNumber("+1234567890")
        .build();

you can provide an extra static unbuild method inside the Employee class. This is useful if you want to have a new Builder with the same fields value of Employee object and then assign different values to certain fields.

import java.util.Date;

public class Employee {
    private final int id;               //required
    private final String name;          //required
    private final String address;       //optional
    private final String phoneNumber;   //optional
    private final Date dateOfBirth;     //optional
    private final String placeOfBirth;  //optional
    private final String ssn;           //optional

    private Employee(Builder builder) {
        this.id = builder.id;
        this.name = builder.name;
        this.address = builder.address;
        this.phoneNumber = builder.phoneNumber;
        this.dateOfBirth = builder.dateOfBirth;
        this.placeOfBirth = builder.placeOfBirth;
        this.ssn = builder.ssn;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public String getPlaceOfBirth() {
        return placeOfBirth;
    }

    public String getSsn() {
        return ssn;
    }

    public Builder unbuild() {
        return new Builder(this);
    }

    public static class Builder {
        private final int id;
        private final String name;
        private String address;
        private String phoneNumber;
        private Date dateOfBirth;
        private String placeOfBirth;
        private String ssn;

        public Builder(int id, String name) {
            this.id = id;
            this.name = name;
        }

        private Builder(Employee employee) {
            this.id = employee.getId();
            this.name = employee.getName();
            this.address = employee.getAddress();
            this.phoneNumber = employee.getPhoneNumber();
            this.dateOfBirth = employee.getDateOfBirth();
            this.placeOfBirth = employee.getPlaceOfBirth();
            this.ssn = employee.getSsn();
        }

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

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

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

        public Builder dateOfBirth(Date dateOfBirth) {
            this.dateOfBirth = dateOfBirth;
            return this;
        }

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

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

The example of unbuild method usage is as follows.

Employee employee = Employee.builder(1, "Watson")
        .address("Baker street, London")
        .phoneNumber("+1234567890")
        .build();

Employee employee2 = employee.unbuild()
        .ssn("123455555")
        .build();

I hope this post gives you a good overview of builder pattern. Next time I’ll discuss other design patterns.