前言
建造者模式,又称 Builder 模式,是一个比较常用的创建型设计模式。建造者模式的原理和代码实现非常简单,掌握起来并不难,难点在于应用场景。比如,有没有考虑过这样的问题:直接使用构造函数或者配合 set 方法就能创建对象,为什么还需要建造者模式来创建呢?建造者模式和工厂模式都可以创建对象,那它们两个的区别在哪?
为什么需要建造者模式
在平常的开发中,创建一个对象最常用的方式,就是使用 new 关键字调用类的构造函数来完成,再者,会配合 set 函数给成员变量赋值。但是这样会有以下的一些问题:
- 对于创建对象中的一些配置项是必填项,我们把它们放到构造函数中,强制创建对象的时候就设置。如果必填项有很多的话,放到构造函数中设置,那么构造函数的参数列表就会很长,代码的可读性和易错性都会变差,也容易搞错各参数的顺序。如果放到 set 函数中设置,校验这些必填项的逻辑就无处安放。
- 对于配置项之间有一定依赖关系的,如设置了某一个配置项,那么就必须要把跟它有依赖关系或约束关系的配置项也显式地设置。那这些配置项之间的依赖关系或约束关系的校验逻辑就无处安放了。
- 对于创建不可变对象,就是在对象创建好之后,就不能修改内部的属性值这种情况,就不能使用 set 方法了。
- 对于一些必须要设置某几个配置项,才能起作用的对象,在通过 set 方法设置完这几个配置项前,对象实例会处于一种不可用的状态。
而 Builder 模式能很好的解决上面的问题,对于配置项校验的问题,我们可以把逻辑都放到 build() 函数中处理。
代码实例:
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>maxIdleResourcePoolConfig config = new ResourcePoolConfig.Builder().setName("dbconnectionpool").setMaxTotal(16).setMaxIdle(10).setMinIdle(12).build()
与工厂模式有何区别
用一个网上经典的例子解释:
顾客走进一家餐馆点餐,我们利用工厂模式,根据用户不同的选择,来制作不同的食物,比 如披萨、汉堡、沙拉。对于披萨来说,用户又有各种配料可以定制,比如奶酪、西红柿、起 司,我们通过建造者模式根据用户选择的不同配料来制作披萨。
工厂模式是用来 创建不同但是相关类型的对象(继承同一父类或者接口的一组子类),由给定的参数来决定 创建哪种类型的对象。建造者模式是用来创建一种类型的复杂对象,可以通过设置不同的可 选参数,“定制化”地创建不同的对象。
实际上,我们也不要太学院派,非得把工厂模式、建造者模式分得那么清楚,我们需要知道 的是,每个模式为什么这么设计,能解决什么问题。只有了解了这些最本质的东西,我们才 能不生搬硬套,才能灵活应用,甚至可以混用各种模式创造出新的模式,来解决特定场景的 问题。
