dimanche 1 mai 2016

How To Implement Builder Pattern in Java? Static or Standalone Class?

I am studying Gang Of Four design patterns and I want to implement a builder pattern in Java. It seems there are two options to build a builder pattern;

  1. Using two different classes: A target class and a builder class
  2. Using a nested static inner builder class inside the target class

1) Using Builder Class As A Separate Class

On both approaches, the target class member variables must be final, thus the target class instances will be immutable.

In this approach, target class will have two constructors. The first constructor will include all member variables of the class as a parameter. And second constructor will take one parameter of its own type, that which builder.build() method returns by using the first constructor of the target class.

1.a) Domain Object Class

package impl.first;

public class DomainObject {

    private final int id;
    private final String name;
    private final String surname;
    private final boolean isMarried;

    // includes all member variables
    public DomainObject(int id, String name, String surname, boolean isMarried) {
        this.id = id;
        this.name = name;
        this.surname = surname;
        this.isMarried = isMarried;
    }

    public DomainObject(DomainObject domainObject) {
        this.id = domainObject.getId();
        this.name = domainObject.getName();
        this.surname = domainObject.getSurname();
        this.isMarried = domainObject.isMarried();
    }

    // getters
    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getSurname() {
        return surname;
    }

    public boolean isMarried() {
        return isMarried;
    }
}

1.b) Domain Object Builder Class

package impl.first;

public final class DomainObjectBuilder {

    private int id;
    private String name;
    private String surname;
    private boolean isMarried;

    // No-arg Default Constructor
    public DomainObjectBuilder() { }

    public DomainObject build() {
        return new DomainObject(id, name, surname, isMarried);
    }

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

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

    public DomainObjectBuilder surname(String surname) {
        this.surname = surname;
        return this;
    }

    public DomainObjectBuilder isMarried(boolean isMarried) {
        this.isMarried = isMarried;
        return this;
    }

}

1.c) Demo Code

package impl.first;

public class DomainObjectDemo {

    public static void main(String[] args) {
        DomainObjectBuilder builder = new DomainObjectBuilder();
        DomainObject dom;

        // Test #1 : None of member variables are set
        System.out.println("Test #1 : None of member variables are set");
        System.out.println("******************************************\n");

        dom = new DomainObject(builder.build());

        printDomObject(dom);

        // Test #2 : Half of objects are set
        System.out.println("Test #2 : Half of objects are set");
        System.out.println("*********************************\n");

        builder.id(1299).name("Levent").surname("Divilioglu").isMarried(false);
        dom = new DomainObject(builder.build());

        printDomObject(dom);

        // Test #3 : All of objects are set
        System.out.println("Test #3 : All of objects are set");
        System.out.println("********************************\n");

        builder.id(1299).name("Levent").surname("Divilioglu").isMarried(false);
        dom = new DomainObject(builder.build());

        printDomObject(dom);
    }

    public static void printDomObject(DomainObject dom) {
        System.out.println("Instance HashCode: " + dom.hashCode());
        System.out.println("Id               : " + dom.getId());
        System.out.println("Name             : " + dom.getName());
        System.out.println("Surname          : " + dom.getSurname());
        System.out.println("isMarried        : " + dom.isMarried());
        System.out.println();
    }
}

1.d) Output

Test #1 : None of member variables are set
******************************************

Instance HashCode: 705927765
Id               : 0
Name             : null
Surname          : null
isMarried        : false

Test #2 : Half of objects are set
*********************************

Instance HashCode: 366712642
Id               : 1299
Name             : Levent
Surname          : Divilioglu
isMarried        : false

Test #3 : All of objects are set
********************************

Instance HashCode: 1829164700
Id               : 1299
Name             : Levent
Surname          : Divilioglu
isMarried        : false

2) Using Builder Class As An Inner Static Class

2.a) Domain Object With Inner Static Builder Class

package impl.second;

public class DomainObject {

    private final int id;
    private final String name;
    private final String surname;
    private final boolean isMarried;

    public DomainObject(Builder domainObject) {
        this.id = domainObject.id;
        this.name = domainObject.name;
        this.surname = domainObject.surname;
        this.isMarried = domainObject.isMarried;
    }

    public static class Builder {
        private int id;
        private String name;
        private String surname;
        private boolean isMarried;

        // No-arg Default Constructor
        public Builder() { }

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

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

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

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

        public Builder isMarried(boolean isMarried) {
            this.isMarried = isMarried;
            return this;
        }
    }

    // getters
    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getSurname() {
        return surname;
    }

    public boolean isMarried() {
        return isMarried;
    }
}

2.b) Demo Code

package impl.second;

public class DomainObjectDemo {

    public static void main(String[] args) {
        DomainObject.Builder builder = new DomainObject.Builder();
        DomainObject dom;

        // Test #1 : None of member variables are set
        System.out.println("Test #1 : None of member variables are set");
        System.out.println("******************************************\n");

        dom = builder.build();

        printDomObject(dom);

        // Test #2 : Half of objects are set
        System.out.println("Test #2 : Half of objects are set");
        System.out.println("*********************************\n");

        builder.id(1299).name("Levent").surname("Divilioglu").isMarried(false);
        dom = builder.build();

        printDomObject(dom);

        // Test #3 : All of objects are set
        System.out.println("Test #3 : All of objects are set");
        System.out.println("********************************\n");

        builder.id(1299).name("Levent").surname("Divilioglu").isMarried(false);
        dom = builder.build();

        printDomObject(dom);
    }

    public static void printDomObject(DomainObject dom) {
        System.out.println("Instance HashCode: " + dom.hashCode());
        System.out.println("Id               : " + dom.getId());
        System.out.println("Name             : " + dom.getName());
        System.out.println("Surname          : " + dom.getSurname());
        System.out.println("isMarried        : " + dom.isMarried());
        System.out.println();
    }
}

2.c) Output

Test #1 : None of member variables are set
******************************************

Instance HashCode: 705927765
Id               : 0
Name             : null
Surname          : null
isMarried        : false

Test #2 : Half of objects are set
*********************************

Instance HashCode: 366712642
Id               : 1299
Name             : Levent
Surname          : Divilioglu
isMarried        : false

Test #3 : All of objects are set
********************************

Instance HashCode: 1829164700
Id               : 1299
Name             : Levent
Surname          : Divilioglu
isMarried        : false

As it is obvious, on two different approach, three Domain Object instances are have different unique hashcodes thus each builder returns unique class instances.

What is the best practice, what are the pros and cons of each approach ?

Aucun commentaire:

Enregistrer un commentaire