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
|
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
|
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.