介绍

建造者模式又叫创建者模式(Builder Pattern),是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。

适用场景

  • 隔离复杂对象的创建和使用,相同的方法,不同的执行顺序,产生不同事件结果。
  • 多个配件都可以装配到一个对象中,但产生不同的运行结果。
  • 产品类非常复杂或者产品类因为调用顺序不同而产生不用结果。
  • 初始化一个对象时,参数过多,或者很多参数具有默认值。
  • 需要生成的产品对象有复杂的内部结构,这些产品对象具有共性。

主要作用

  • 在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
  • 用户只需要给出指定复杂对象的类型和内容。
  • 建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)。

类图

image-20220214095028053

类图讲解

  • 指挥者(Director)直接和客户(Client)进行需求沟通。
  • 沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求(Builder)。
  • 将各个部件的建造请求委派到具体的建造者(ConcreteBuilder)。
  • 各个具体建造者负责进行产品部件的构建。
  • 最终构建成具体产品(Product)。

代码示例

我们用生产共享单车的例子来更好的理解一下建造者模式。

产品类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Bicycle {

//名字
private String name;
//边框
private Frame frame;
//座椅
private Seat seat;
//轮胎
private Tire tire;

public String getName() {
return name;
}
//省略get、set方法
}

此处边框、座椅、轮胎都采用接口的形式,更方便产品属性的扩展

Frame类

1
2
3
4
5
6
public interface Frame {

//材质
void texture();

}

Frame实现类

1
2
3
4
5
6
public class AlloyFrame implements Frame {
@Override
public void texture() {
System.out.println("合金材质...");
}
}
1
2
3
4
5
6
7
public class SteelFrame implements Frame {

@Override
public void texture() {
System.out.println("钢材质...");
}
}

Seat实现类

1
2
3
4
5
6
7
public class dermisSeat implements Seat {

@Override
public void texture() {
System.out.println("真皮座椅...");
}
}
1
2
3
4
5
6
7
public class plasticSeat implements Seat {

@Override
public void texture() {
System.out.println("塑料座椅...");
}
}

Tire实现类

1
2
3
4
5
6
public class BigTire implements Tire {
@Override
public void texture() {
System.out.println("大轮胎...");
}
}
1
2
3
4
5
6
7
public class LittleTire implements Tire {

@Override
public void texture() {
System.out.println("小轮胎...");
}
}

Builder类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface Builder {

//名字
void buildName();

//构造边框,框架
void buildFrame();

//构造座椅
void buildSeat();

//构造轮胎
void buildTire();

//返回构造好的自行车对象
Bicycle builder();

}

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
public class MobikeBuilder implements Builder {

private Bicycle bicycle = new Bicycle();


@Override
public void buildName() {
bicycle.setName("摩拜单车...");
}

@Override
public void buildFrame() {
bicycle.setFrame(new SteelFrame());
}

@Override
public void buildSeat() {
bicycle.setSeat(new dermisSeat());
}

@Override
public void buildTire() {
bicycle.setTire(new BigTire());
}

@Override
public Bicycle builder() {
return this.bicycle;
}
}
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 OfoBuilder implements Builder {

private Bicycle bicycle = new Bicycle();

@Override
public void buildName() {
this.bicycle.setName("ofo单车...");
}

@Override
public void buildFrame() {
this.bicycle.setFrame(new AlloyFrame());
}

@Override
public void buildSeat() {
this.bicycle.setSeat(new plasticSeat());
}

@Override
public void buildTire() {
this.bicycle.setTire(new LittleTire());
}

@Override
public Bicycle builder() {
return this.bicycle;
}
}

指挥者Director类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Director {

private Builder builder;

public Director(Builder builder){
this.builder = builder;
}

public Bicycle builder(){
builder.buildName();
builder.buildFrame();
builder.buildSeat();
builder.buildTire();
return builder.builder();
}
}

测试截图

image-20220214171230778

上面示例是 Builder模式的常规用法,指挥者类 Director 在 Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类。还有另外一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用 builder模式进行重构。

重构前示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Computer {

private String cpu;
private String screen;
private String memory;
private String mainboard;
public Computer(String cpu, String screen, String memory, String mainboard) {
this.cpu = cpu;
this.screen = screen;
this.memory = memory;
this.mainboard = mainboard;
}

//省略get、set、toString方法
}

重构后的示例代码:

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
public class NewComputer {

private String cpu;
private String screen;
private String memory;
private String mainboard;
//构造器创建对象是直接抛出异常
public NewComputer() {
throw new RuntimeException("can’t init");
}
//私有化的有参构造器,只能通过Builder类来构建NewComputer类
private NewComputer(Builder builder) {
cpu = builder.cpu;
screen = builder.screen;
memory = builder.memory;
mainboard = builder.mainboard;
}

//静态内部类,决定了怎么构建NewComputer类
public static final class Builder {
private String cpu;
private String screen;
private String memory;
private String mainboard;

public Builder() {}

public Builder cpu(String cpu) {
this.cpu = cpu;
//返回this方便链式调用
return this;
}
public Builder screen(String screen) {
this.screen = screen;
return this;
}
public Builder memory(String memory) {
this.memory = memory;
return this;
}
public Builder mainboard(String mainboard) {
this.mainboard = mainboard;
return this;
}
public NewComputer build() {
return new NewComputer(this);}
}
}

测试截图

image-20220214173915126

上面的示例代码只是传入四个参数,如果参数是十个甚至更多,builder 模式的优势将会更加明显,传递参数更加灵活,代码具有更高的可读性.

优点

  • 使用建造者模式可以使客户端不必知道产品内部组成的细节。
  • 具体的建造者类之间是相互独立的,这有利于系统的扩展。
  • 具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响。

缺点

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

建造者模式与抽象工厂模式的比较

  • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族 。
  • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象 。
  • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。