Contents

Creating complex objects using the Builder Pattern

Overview

The builder creational design pattern is used to create a complex object, in a step by step manner.

You might have come across the same using while using some Java Clients or any other framework, lets us now see what problem it solves.

The core idea here is that if we need to send too many parameters in a constructor call,its hard to maintain the order of the call, and the constructor call itself is very difficult to read. And in most cases all the parameters are not necessary for object initialization and can be skipped instead of passing mandatory fields when object is created via a constructor.

And creating different constructors to cater to each and every scenario is a pain you dont want to deal with.

To see the difference lets try creating a somewhat complex (pardon me if it doesnt seem as complex, i only order plain vanilla) ice cream object with traditional constructor methods vs the Builder Pattern Approach.

Code Example using normal constructors

Creating an object via the conventional method

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

public class IceCream {

    // Mandatory Fields
    private int quantityInGrams;
    private String flavour;

    // Optional Fields
    private String topping;
    private boolean islactoseIntolerant;
    private String slicedFruitType;


    public IceCream(int quantityInGrams, String flavour, String topping, boolean islactoseIntolerant, String slicedFruitType) {
        this.quantityInGrams = quantityInGrams;
        this.flavour = flavour;
        this.topping = topping;
        this.islactoseIntolerant = islactoseIntolerant;
        this.slicedFruitType = slicedFruitType;
    }
}


public static void main(String[] args) {

        IceCream iceCream = new IceCream(50,"Vanilla",null,false,null);
}


Code example using the builder pattern

Creating the above ice cream object and instantiating it via the Builder patten

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

package icecream;

public class IceCream {

    // Mandatory Fields
    private int quantityInGrams;
    private String flavour;

    // Optional Fields
    private String topping;
    private boolean islactoseIntolerant;
    private String slicedFruitType;

    public IceCream(IceCreamBuilder builder){
        this.quantityInGrams=builder.quantityInGrams;
        this.flavour=builder.flavour;
        this.islactoseIntolerant=builder.islactoseIntolerant;
        this.topping=builder.topping;
    }

    public static class IceCreamBuilder{

        // Mandatory Fields
        private int quantityInGrams;
        private String flavour;

        // Optional Fields
        private String topping;
        private boolean islactoseIntolerant;
        private String slicedFruitType;

        public IceCreamBuilder(int quantityInGrams,String flavour){
            this.quantityInGrams = quantityInGrams;
            this.flavour = flavour;
        }

        public IceCreamBuilder setTopping(String topping) {
            this.topping = topping;
            return this;
        }

        public IceCreamBuilder setIslactoseIntolerant(boolean islactoseIntolerant) {
            this.islactoseIntolerant = islactoseIntolerant;
            return this;
        }

        public IceCreamBuilder setSlicedFruitType(String slicedFruitType) {
            this.slicedFruitType = slicedFruitType;
        }

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


  IceCream iceCream = new IceCream.
                            IceCreamBuilder(50,"Vanilla").
                            setIslactoseIntolerant(false).
                            setTopping("choclate").
                            setSlicedFruitType("mango").
                            build();


Conclusion

The obvious difference when choosing the builder pattern approach as you can see above is readibility, object instantitation is readable as compared to using a constructor call. Its also easier to send in wrong parametrs of the same type when passing a large number of arguments via a constructor which can be avoided via the bulder pattern.

It also absctracts away optional parameters the user might not want to set.