建造者模式(Builder Pattern)是一个比较常用的创建型设计模式,又叫Builder 模式,中文翻译为建造者模式或者构建者模式,也有人叫它生成器模式。
定义
英文定义:
The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.
翻译成中文的大概意思就是:
Builder 设计模式的目的是将复杂对象的构造与其表示分离。通过这样做,相同的构建过程可以创建不同的表示。
为什么需要建造者模式
首先,我们知道,建造者模式也是属于创建型的设计模式,所以它的目的也是为了创建一个类的,那什么时候需要我们用到建造者模式,而不是用我们经常使用的new(Java)方式来创建呢?
当使用new的方式创建的一个复杂对象时,初始化的参数我们一般都是通过构造函数来传递的,当参数过多、有必填参数校验、参数有依赖(比如参数A填了后参数B就必填,否则参数B不必填等)等多种复杂情况时,使用起来就不是那么优雅了。
参数过多,构造函数参数列表就会边长,代码的可读写和易用性就会变得很差,而且在使用的时候还很容易搞错参数的顺序,从而导致不容易发现的BUG。
为了解决上面的一系列问题,就引入了一个Builder类,我们把参数的必填等校验逻辑放入到Builder类中,我们创建类的时候先创建Builder(建造者)类,然后通过set方法设置建造者的变量,最后使用build()方法来做最后的集中校验,校验通过才会创建我们的目标对象。
明白了为什么要使用建造者模式后,下面看一个代码示例。
示例
下面是一个资源池配置类 ResourcePoolConfig,有4个入参。
-
使用new的方式构造对象:
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方法... }
参数过多时使用构造方法创建对象:
// 参数太多,导致可读性差、参数可能传递错误 ResourcePoolConfig config = new ResourcePoolConfig("dbconnectionpool", 16, null, 8, null, false , true, 10, 20,false, true);
-
使用Builder模式
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();
与工厂模式的区别
- 工厂模式主要是用来创建不同但是类型相关的对象(继承同一父类或者接口的一组子类),由给定的参数来决定创建哪种类型的对象。
- 建造者模式是用来创建一种类型的复杂对象,通过设置不同的可选参数,“定制化”地创建不同的对象。
网上有一个经典的例子很好地解释了两者的区别。
顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比如披萨、汉堡、沙拉。
对于具体的披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起司,我们通过建造者模式根据用户选择的不同配料来制作披萨。
实际上,我们也不要太学院派,非得把工厂模式、建造者模式分得那么清楚,我们需要知道的是,每个模式为什么这么设计,能解决什么问题。只有了解了这些最本质的东西,我们才能不生搬硬套,才能灵活应用,甚至可以混用各种模式创造出新的模式,来解决特定场景的问题。
设计模式系列阅读目录
- 设计模式:创建型—单例模式
- 设计模式:创建型—原型模式
- 设计模式:创建型—工厂模式
- 设计模式:创建型—建造者模式
- 设计模式:结构型—享元模式
- 设计模式:结构型—组合模式
- 设计模式:结构型—代理模式
- 设计模式:结构型—桥接模式
- 设计模式:结构型—装饰器模式
- 设计模式:结构型—适配器模式
- 设计模式:结构型—门面模式
- 设计模式:行为型—中介模式
- 设计模式:行为型—命令模式
- 设计模式:行为型—备忘录模式
- 设计模式:行为型—模板模式
- 设计模式:行为型—状态模式
- 设计模式:行为型—策略模式
- 设计模式:行为型—职责链模式
- 设计模式:行为型—观察者模式
- 设计模式:行为型—解释器模式
- 设计模式:行为型—访问者模式
- 设计模式:行为型—迭代器模式