Builder in Java and Ruby

Builder in Java and Ruby

Recently I’ve been context-switching between 2 different projects – one in Ruby and the other in Java – and I got a chance to utilize Builder Pattern (to be precise, it’s a variant of the classic builder pattern) in Java, and get impressed by how much simpler the solution could possibly be in Ruby.

The Problem

The goal is to create immutable objects with a mixture of mandatory and optional attributes. To simplify, all attributes are primitive types or String.

Here I use a Person class as example, with following attributes:

// required fields
String firstName;
String lastName;
// optional fields
int age;
String address;
String email;

In Java

Naive Approach: Overloading Constructor

A naive implementation is to create overloaded constructors:

public class Person {
   // fields omitted
  public Person(String firstName, String lastName, int age, String address, String email) {...}
  public Person(String firstName, String lastName, int age, String address) {...}
  public Person(String firstName, String lastName, int age) {...}
  public Person(String firstName, String lastName) {...}
  ...
}

This can become very complicated, as number of fields increases, which results in a lot more possible combinations of required and optional attributes in arguments.

With PersonBuilder

It’s not the best fit for classic Builder Pattern, but a good use case for one of its variants. The key responsibility of the builder in this case is the attributes setter of the Person class. The general signature of the set method is:

public PersonBuilder setAttributeA(AttributeA attribute)

It also return the builder self so that the set of methods can be chained. Full code example for Person class:

package BuilderPattern.BeanStyleExample;

public class Person {
    //    required fields
    private final String firstName;
    private final String lastName;
    //    optional fields
    private int age;
    private String address;
    private String email;

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

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

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

    public String getEmail() {
        return email;
    }

    void setEmail(String email) {
        this.email = email;
    }
}

Class diagram:

A few notes:

  • required fields (firstName and lastName) are marked as final and are set in constructor;
  • the access for constructor and setters are set as package private. In this case, they can only be called by PersonBuilder, which is in the same package, but not directly by the external client.

Code for PersonBuilder:

package BuilderPattern.BeanStyleExample;

public class PersonBuilder {
    private Person person;

    public PersonBuilder(String firstName, String lastName) {
        person = new Person(firstName, lastName);
    }

    public PersonBuilder setAge(int age) {
        person.setAge(age);
        return this;
    }

    public PersonBuilder setAddress(String address){
        person.setAddress(address);
        return this;
    }

    public PersonBuilder setEmail(String email){
        person.setEmail(email);
        return this;
    }

    public Person build(){
        return person;
    }
}

To create a Person, a client need to supply required attributes (firstName and lastName) into constructor of PersonBuilder, and optionally call “setter”s of the builder that internally delegate to the Person class. At last, when the client call build(), it receives the Person object with read-only access.

In Ruby

Builder may not even be necessary

The same problem can be solved in an incredibly simple way without builder getting involved at all, thanks to Ruby’s keyword arguments:

class Person
  attr_reader :first_name, :last_name, :age, :address, :email
  def initialize(first_name:,
                 last_name:,
                 age: 0,
                 address: 'default address',
                 email: 'default@example.com')
    @first_name = first_name
    @last_name = last_name
    @age = age
    @address = address
    @email = email
  end
end

And client can pass keyword arguments with all required fields and with/without any optional fields:

person = Person.new(first_name: 'John', last_name: 'Doe', email: 'test@example.com')

puts "Hi! My name is #{person.first_name} #{person.last_name}."
puts person.inspect

# Hi! My name is John Doe.
# <Person:0x00007fb96a1aa420 @first_name="John", @last_name="Doe", @age=0, @address="default address", @email="test@example.com">

Builder pattern can be applied in Ruby, of course, especially when the members are complex objects, rather than just primitive types. It’s just that our use case is too simple to worth a dedicated builder class.

Additional Reading

Credit: featured image source: link

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.