我们明明可以直接使用构造函数或者配合set方法就能创建对象, 为什么还需要通过建造者模式来创建呢.

建造者模式和工厂模式都可以创建对象, 他们之间的区别是什么?

为什么需要建造者模式

在某些配置类中, 有大量的参数需要传入, 并且这些参数有一些是必须的, 有一些是可选的.

某些参数之间有依赖关系, 比如当用户设置了A, 那么就必须设置B等等.

并且我们希望类对象是不可变对象, 也就是说对象在创建好之后, 就不能修改内部的属性值. 要实现这个功能, 我们就不能暴露set方法.

这时我们对这个类的创建虽然还可以通过构造函数来进行创建, 但是复杂度和可读性都不友好. 这时就可以使用建造者模式来进行对象的创建.

将校验逻辑放到Builder类中, 先创建建造者, 并通过set方法来设置建造者的变量值, 然后在build方法真正创建对象之前, 做集中的校验, 校验通过之后才会创建对象. 并且将类的构造函数设置成private, 这样就只能通过建造者来创建对象. 同时将不再提供set方法, 这样创建出来的对象就是不可变对象了

实现

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
 public class ResourcePoolConfig {
   private String name;
   private int maxTotal;
   private int maxIdle;
   private int minIdle;
 
   private ResourcePoolConfig(Builder builder) {
     this.name = builder.name;
     this.maxTotal = builder.maxTotal;
     this.maxIdle = builder.maxIdle;
     this.minIdle = builder.minIdle;
  }
   //...省略getter方法...
 
   //我们将Builder类设计成了ResourcePoolConfig的内部类。
   //我们也可以将Builder类设计成独立的非内部类ResourcePoolConfigBuilder。
   public static class Builder {
     private static final int DEFAULT_MAX_TOTAL = 8;
     private static final int DEFAULT_MAX_IDLE = 8;
     private static final int DEFAULT_MIN_IDLE = 0;
 
     private String name;
     private int maxTotal = DEFAULT_MAX_TOTAL;
     private int maxIdle = DEFAULT_MAX_IDLE;
     private int minIdle = DEFAULT_MIN_IDLE;
 
     public ResourcePoolConfig build() {
       // 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
       if (StringUtils.isBlank(name)) {
         throw new IllegalArgumentException("...");
      }
       if (maxIdle > maxTotal) {
         throw new IllegalArgumentException("...");
      }
       if (minIdle > maxTotal || minIdle > maxIdle) {
         throw new IllegalArgumentException("...");
      }
 
       return new ResourcePoolConfig(this);
    }
 
     public Builder setName(String name) {
       if (StringUtils.isBlank(name)) {
         throw new IllegalArgumentException("...");
      }
       this.name = name;
       return this;
    }
 
     public Builder setMaxTotal(int maxTotal) {
       if (maxTotal <= 0) {
         throw new IllegalArgumentException("...");
      }
       this.maxTotal = maxTotal;
       return this;
    }
 
     public Builder setMaxIdle(int maxIdle) {
       if (maxIdle < 0) {
         throw new IllegalArgumentException("...");
      }
       this.maxIdle = maxIdle;
       return this;
    }
 
     public Builder setMinIdle(int minIdle) {
       if (minIdle < 0) {
         throw new IllegalArgumentException("...");
      }
       this.minIdle = minIdle;
       return this;
    }
  }
 }
 
 // 这段代码会抛出IllegalArgumentException,因为minIdle>maxIdle
 ResourcePoolConfig config = new ResourcePoolConfig.Builder()
        .setName("dbconnectionpool")
        .setMaxTotal(16)
        .setMaxIdle(10)
        .setMinIdle(12)
        .build();

为了避免这种无效状态的存在, 我们就需要使用构造函数一次性初始化好所有的成员变量. 如果构造函数参数过多, 我们就需要考虑使用建造者模式, 先设置建造者的变量, 然后再一次性的创建对象, 让对象一直处于有效状态.

使用建造者模式创建对象, 还能避免对象存在无效状态. 假如我们定义了一个长方形, 如果不使用建造者模式, 而是使用set的方式, 那么在调用第一个set之后和调用第二个set之前, 这个对象是处于无效状态的.

与[[工厂模式]]的区别

建造者模式创建的是同一种类型的复杂对象, 通过设置不同的可选参数, 来定制化的创建不同的对象

而工厂模式则是创建不同但是相关类型的对象(继承同一父类或者接口的一组子类), 通过给定的参数来决定创建哪种类型的对象.