概述

The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.

将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示


使用场景

  • 当一个类的构造参数个数超过了4个,而且这些参数有些是可选的参数,考虑使用构造者模式;
  • 当创建复杂对象的算法应该独立与该对象的组成部分以及它们的装配方式时;
  • 当构造过程中允许被构造的对象有不同的表示时。

Builder设计模式中涉及到的参与者

  • Builder
    • 为创建一个Product对象的各个部件指定抽象接口。
  • ConcreteBuilder
    • 实现Builder的接口以构造和装配该产品的个部件。
    • 定义并明确它所创建的表示,即针对不同的商业逻辑,具体化复杂对象的各部分的创建
    • 提供一个检索产品的接口。
    • 构造一个使用Builder接口的对象即在指导者的调用下创建产品实例
  • Director
    • 构造一个使用Builder接口的对象。
  • Product
    • 表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
    • 包含定义组成部件的类,包括将这些部件装配成最中产品的接口。

其实,在实际使用时,可以根据需要对参与者进行简化或删减,最小结构应该至少包括Builder和Product。

代码实现

  • User类并实现
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class User {
//All final attributes
private final String firstName; // required
private final String lastName; // required
private final int age; // optional
private final String phone; // optional
private final String address; // optional

private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}

//All getter, and NO setter to provide immutability
public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}

public int getAge() {
return age;
}

public String getPhone() {
return phone;
}

public String getAddress() {
return address;
}

@Override
public String toString() {
return "User: " + this.firstName + ", " + this.lastName + ", " + this.age + ", " + this.phone + ", " + this.address;
}

public static class UserBuilder {
private final String firstName;
private final String lastName;
private int age;
private String phone;
private String address;

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

public UserBuilder age(int age) {
this.age = age;
return this;
}

public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}

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

//Return the finally consrcuted User object
public User build() {
User user = new User(this);
validateUserObject(user);
return user;
}

private void validateUserObject(User user) {
//Do some basic validations to check
//if user object does not break any assumption of system
}
}
}

  • 使用
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
public static void main(String[] args) {
User user1 = new User.UserBuilder("Lokesh", "Gupta")
.age(30)
.phone("1234567")
.address("Fake address 1234")
.build();

System.out.println(user1);

User user2 = new User.UserBuilder("Jack", "Reacher")
.age(40)
.phone("5655")
//no address
.build();

System.out.println(user2);

User user3 = new User.UserBuilder("Super", "Man")
//No age
//No phone
//no address
.build();

System.out.println(user3);
}
/*
output:

User: Lokesh, Gupta, 30, 1234567, Fake address 1234
User: Jack, Reacher, 40, 5655, null
User: Super, Man, 0, null, null

*/

JDK源码中的实现

  • All implementations of java.lang.Appendable are infact good example of use of Builder pattern in java. e.g.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    StringBuilder builder = new StringBuilder("Temp");
    String data = builder.append(1)
    .append(true)
    .append("friend")
    .toString();
    System.out.println(data);
    /** Output:
    Temp1truefriend
    */
  • javax.swing.GroupLayout.Group#addComponent()的使用

Lombok中 @Builder注解内部实现

1
2
3
4
5
6
7
@Builder
public class User {
private final Integer code = 200;
private String username;
private String password;
}

Builder内部帮我们做了什么

  • 创建一个名为ThisClassBuilder的内部静态类,并具有和实体类形同的属性(称为构建器)。
  • 在构建器中:对于目标类中的所有的属性和未初始化的final字段,都会在构建器中创建对应属性。
  • 在构建器中:创建一个无参的default构造函数。
  • 在构建器中:对于实体类中的每个参数,都会对应创建类似于setter的方法,只不过方法名与该参数名相同。 并且返回值是构建器本身(便于链式调用),如上例所示。
  • 在构建器中:一个build()方法,调用此方法,就会根据设置的值进行创建实体对象。
  • 在构建器中:同时也会生成一个toString()方法。
  • 在实体类中:会创建一个builder()方法,它的目的是用来创建构建器。

编译后

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
public class User {
private String username;
private String password;
User(String username, String password) {
this.username = username; this.password = password;
}
public static User.UserBuilder builder() {
return new User.UserBuilder();
}

public static class UserBuilder {
private String username;
private String password;
UserBuilder() {}

public User.UserBuilder username(String username) {
this.username = username;
return this;
}
public User.UserBuilder password(String password) {
this.password = password;
return this;
}
public User build() {
return new User(this.username, this.password);
}
public String toString() {
return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ")";
}
}
}