之前,我们学习了工厂模式,讲解了工厂模式的应用常见,现在我们学习另一个常见的创建型设计模式,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.与工厂模式有什么区别吗?

工厂模式是用来创建不同实例,但是类型相同的对象,就是继承了统一父类,或者实现了同一个接口的一组类,由给定的参数来决定使用哪个类型的对象,建造者模式是用来创建一种类型的复杂对象,支持高度定制化这个对象

而且,没有必要太过于学院派,根据不同的场景,设置不同的模式,要做到灵活应用才可以解决特定的问题

本章的重点很简单了

就是对于建造者模式的简单讲解,如果一个类中,有很多的属性,为了避免构造函数的参数列表过长,太难以写,可以使用建造者模式

而且对于下面的三种情况,可以考虑使用建造者模式了

将必填项放入构造函数,如果参数多,就可以考虑使用建造者了

类的属性之间有一定的约束,可以考虑使用建造者模式了

希望创建不可变的对象,在对象创建好的时候,就不修改内部的属性值了,就可以使用建造者模式

发表评论

邮箱地址不会被公开。 必填项已用*标注