之前,我们学习了工厂模式,讲解了工厂模式的应用常见,现在我们学习另一个常见的创建型设计模式,Builder模式,可以叫做建造者模式或者构建者模式
今天我们主要学习的是,建造模式和工厂模式的的区别,为什么要用建造者模式
1.为什么要用建造者模式
一般来说,使用new关键字就能生成类,但是为什么还需要建造者模式呢?
假设有如下的一个常见,需要定义一个资源配置类ResourcePoolConfig,需要的实现如下
那么这个代码很简单,
public class ResourcePoolConfig {
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(String name, Integer maxTotal, Integer maxIdle, Integer minIdle) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException(“name should not be empty.”); } this.name = name; if (maxTotal != null) { if (maxTotal <= 0) { throw new IllegalArgumentException(“maxTotal should be positive.”); } this.maxTotal = maxTotal; } if (maxIdle != null) { if (maxIdle < 0) { throw new IllegalArgumentException(“maxIdle should not be negative.”); } this.maxIdle = maxIdle; } if (minIdle != null) { if (minIdle < 0) { throw new IllegalArgumentException(“minIdle should not be negative.”); } this.minIdle = minIdle; } } //…省略getter方法… } |
如果直接这样写的话,就必须传递几个null值来获取实例了,而且代码的书写也会增长
而且,伴随着参数的增加,其参数列表也会变长,构造函数也会变长,所以我们可以直接在构造函数中只传入一个name,剩下的通过setter进去
public class ResourcePoolConfig {
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(String name) { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException(“name should not be empty.”); } this.name = name; } public void setMaxTotal(int maxTotal) { if (maxTotal <= 0) { throw new IllegalArgumentException(“maxTotal should be positive.”); } this.maxTotal = maxTotal; } public void setMaxIdle(int maxIdle) { if (maxIdle < 0) { throw new IllegalArgumentException(“maxIdle should not be negative.”); } this.maxIdle = maxIdle; } public void setMinIdle(int minIdle) { if (minIdle < 0) { throw new IllegalArgumentException(“minIdle should not be negative.”); } this.minIdle = minIdle; } //…省略getter方法… } |
但是,我们继续往下深入
如果我们希望解决如下三个问题的时候,普通的构造函数就不够使唤
1.如果必填项很多,那么构造参数也会很多,如果我们将必填项通过set设置,那么必填项的校验就没法测试了
2.配置项之间有一定的依赖关系,如果用户设置了最大,就必须设置最小,那么这就要有一定的约束关系,同理最大的要大于最小的
3.如果希望这个配置类是不可变的,我对象创建好之后,就不能再修改内部的属性值,要实现这个功能,就不能暴露set()方法
这就建造者模式的用处了
我们将逻辑校验放在builder类中的build函数中,并且通过set()方法设置建造者变量值,
然后将上面的配置类的构造函数设置为private私有权限,这样就能通过建造者来创建Config对象了
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();
而且有些时候,使用建造者模式创建对象,能够有效的避免对象处于一个无效的状态,因为有些对象,要求两个属性都有的时候,才能生效,在只设置一个时候,让对象逃逸了,就可能出现无效的状态.使用建造者模式,可以在生成类之前,进行相对应的检查
2.与工厂模式有什么区别吗?
工厂模式是用来创建不同实例,但是类型相同的对象,就是继承了统一父类,或者实现了同一个接口的一组类,由给定的参数来决定使用哪个类型的对象,建造者模式是用来创建一种类型的复杂对象,支持高度定制化这个对象
而且,没有必要太过于学院派,根据不同的场景,设置不同的模式,要做到灵活应用才可以解决特定的问题
本章的重点很简单了
就是对于建造者模式的简单讲解,如果一个类中,有很多的属性,为了避免构造函数的参数列表过长,太难以写,可以使用建造者模式
而且对于下面的三种情况,可以考虑使用建造者模式了
将必填项放入构造函数,如果参数多,就可以考虑使用建造者了
类的属性之间有一定的约束,可以考虑使用建造者模式了
希望创建不可变的对象,在对象创建好的时候,就不修改内部的属性值了,就可以使用建造者模式