一. 建造者模式的产生
-
现在, 我们需要定义一个资源池配置类 ResourcePoolConfig
该类有三个属性 maxTotal、maxIdle、minIdle. 这3个参数本身有默认值 DEFAULT_MAX_TOTAL, DEFAULT_MAX_IDLE, DEFAULT_MIN_IDLE, 所以不是必填参数, 不需要在构造函数中初始化,应该放在 setter() 中配置. 根据上面的分析有如下代码: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 (name.equals("")) { throw new IllegalArgumentException("name should not be empty."); } } public void setMaxTotal(int maxTotal) { this.maxTotal = maxTotal; } public void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; } public void setMinIdle(int minIdle) { this.minIdle = minIdle; } }
-
如果 maxTotal、maxIdle、minIdle 这三个参数是相关联的, 只要设置其中一个, 就必须设置另外两个. 这种情况下, setter 函数必须连续调用, 否则会造成中间状态不可用. 且三个参数设置的值要校验为正数才有意义. 所以:
- 我们可以把校验逻辑放置到 Builder 类中,先创建建造者,并且通过 set() 方法设置建造者的变量值,
- 然后再使用 build() 方法真正创建对象之前,做集中的校验,校验通过之后才会创建对象。
- 除此之外,我们把 ResourcePoolConfig 的构造函数改为 private 私有权限。这样我们就只能通过建造者来创建 ResourcePoolConfig 类对象。并且,ResourcePoolConfig 没有提供任何 set() 方法,这样我们创建出来的对象就是不可变对象了。
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; } } }
二. 为什么使用建造者模式
1. 直接使用构造函数或者配合 set 方法就能创建对象,为什么还需要建造者模式来创建呢?
实际上,如果我们并不是很关心对象是否有短暂的无效状态,也不是太在意对象是否是可变的。比如,对象只是用来映射数据库读出来的数据,那我们直接暴露 set() 方法来设置类的成员变量值是完全没问题的。而且,使用建造者模式来构建对象,代码实际上是有点重复的,ResourcePoolConfig 类中的成员变量,要在 Builder 类中重新再定义一遍。
2. 建造者模式和工厂模式都可以创建对象,那它们两个的区别在哪里呢?
实际上,工厂模式是用来创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
网友评论