建造者模式

什么是构建器以及为什么要使用构建器

假设某个类,现在有3个必选属性,有5个可选属性.(为了代码简洁,后面都只写一个必选属性,2个可选属性.懂就行).

那么现在想提供完善的创建该类的机制,该怎么办呢?

首先是方法1-使用重叠的构造方法.

重叠的构造方法

这是大家都熟悉的方法,重载很多个构造方法,每个的参数都不一样,这样的复杂度比较大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Student {

// 必选
String name;
// 可选
int age;
String title;

public Student(String name) {
this.name = name;
}

public Student(String name, int age) {
this.name = name;
this.age = age;
}

public Student(String name, int age, String title) {
this.name = name;
this.age = age;
this.title = title;
}
}

三个构造方法是不是已经比较复杂了,30个属性的类也不少见噢,写死人了要.

而且这样还有一个缺点:可读性太差了,在调用的时候你会看到编译器提醒你有30个构造方法可以调用,并且只显示参数类型不显示参数名字(比如一个8个int参数的构造方法,鬼知道应该按照什么顺序传入啊),你根本不知道该怎么用….

那么还有第二种方法:

javabean,即使用setter.

对每个属性都提供set方法,当新建一个类之后,就可以调用对应的set方法对属性设值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Student {

// 必选
private String name;
// 可选
private int age;
private String title;

public void setName(String name) {
this.name = name;
}

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

public void setTitle(String title) {
this.title = title;
}
}

这样的调用方式,每一个set是一行,非常不简洁

Builder模式

建造者模式第一次接触是在阿里实习的时候,当时使用guava来实现localCache,所谓的建造者模式就是创建类并设值其各filed的方法,举个例子

1
2
3
4
5
6
public class A{
private int a;
private int b;
private String c;
}
A x = new A.builder().a(1).b(2).c("ok");

那么如何实现这样的方式呢?

如果自己实现的话,可以借助静态内部类,把类的构造函数变为私有的,传入的参数是这个静态内部类Builder,静态内部类按照各个属性进行build,返回值都是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
33
34
35
36
37
38
39
40
41
public class Student {

// 必选
private String name;
// 可选
private int age;
private String title;

public static class Builder {
// 必选
private String name;
// 可选
private int age;
private String title;

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

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

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

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

}

private Student(Builder builder) {
name = builder.name;
age = builder.age;
title = builder.title;
}

调用方使用方法如下:

1
2
3
public static void main(String[] args) {
Student s = new Builder("xx").age(11).title("888").build();
}

这样写当然是有弊端的:

  1. 在创建的过程中多创建了一个对象,这对性能肯定是有影响的,所以在极限要求性能的场景可以注意一下.
  2. 代码比重叠构造器的代码都多,写起来复杂度很大

lombok实现建造者模式

那么有一个比较好的方法就是lombok来实现,直接在类上面@Builder就可以实现了,

1
2
3
4
5
@Builder
public class A{
private String name;
private int age;
}

调用方法如下:

1
2
3
public static void main(String[] args) {
A a = A.builder().name("xxx").age(10).build();
}

但是这样的lombok存在一个问题,就是在继承的时候,父类的构造字段不会被子类继承

1
2
3
4
5
6
7
8
9
10
11
12
13
@Getter
@AllArgsConstructor
public class Parent {
private final String parentName;
private final int parentAge;
}

@Getter
@Builder
public class Child extends Parent {
private final String childName;
private final int childAge;
}

在使用Child类的builder的时候,无法继承parent的parentName和parentAge字段

解决方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Getter
@AllArgsConstructor
public class Parent {
private final String parentName;
private final int parentAge;
}

@Getter
public class Child extends Parent {
private final String childName;
private final int childAge;

@Builder
public Child(String parentName, int parentAge, String childName, int childAge) {
super(parentName, parentAge);
this.childName = childName;
this.childAge = childAge;
}
}

但这种方法比较复杂,在lombok1.18版本后,出现一个新的注解是@superBuilder,这个注解解决了这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
@SuperBuilder
public class Parent {
// same as before...

@Getter
@SuperBuilder
public class Child extends Parent {
// same as before...

@Getter
@SuperBuilder
public class Student extends Child {
// same as before...