学习设计模式-创建型-建造者模式

137 阅读2分钟

直接使用构造函数或者配合set方法就能创建对象,为什么还需要建造者来创建?建造者和工厂模式都可以创建对象,他们两者区别是什么

为什么需要建造者模式

假设场景:需要定义资源配置类ResourcePoolConfig,这里的资源池就好比是线程池、连接池、对象池。在这个资源配置类中有以下配置项

image.png 存在问题:

  • name为必填项,可以放到构造函数中,但是如果必填项很多构造函数参数就会很多,如果必填项通过set()方法设置那校验这些必填逻辑就无处安放。
  • 假设这些配置项之间有依赖关系,比如设置了maxTotal、maxIdle、minIdle其中一项就必须显示设置另外两项,那这些校验逻辑在哪设置
  • 如果希望ResouPoolConfig是不可变对象,创建好后不能改变内部属性,那就不能暴露set()方法

我们可以把校验逻辑放到builder中,先创建建造者,通过set设置建造者变量,然后再使用build()创建真正对象前进行校验,然后把ResourcePoolConfig构造函数设置为private

public class ResourcePoolConfig {

   @Getter
   private String name;
   @Getter
   private int maxTotal;
   @Getter
   private int maxIdle;
   @Getter
   private int minIdle;

   private ResourcePoolConfig(Builder builder) {
      this.name = builder.name;
      this.maxTotal = builder.maxTotal;
      this.maxIdle = builder.maxIdle;
      this.minIdle = builder.minIdle;
   }

   @Accessors(chain = true)
   public static class Builder {

      private static final int DEFAULT_MAX_TOTAL = 8;
      private static final int DEFAULT_MAX_IDLE = 0;
      private static final int DEFAULT_MIN_IDLE = 0;
      @Setter
      private String name;
      @Setter
      private int maxTotal = DEFAULT_MAX_TOTAL;
      @Setter
      private int maxIdle = DEFAULT_MAX_IDLE;
      @Setter
      private int minIdle = DEFAULT_MIN_IDLE;

      public ResourcePoolConfig build() {
         //属性检验逻辑...
         if (StringUtils.isBlank(name)) {
            throw new IllegalArgumentException("...");
         }
         if (maxIdle > maxTotal) {
            throw new IllegalArgumentException("...");
         }
         return new ResourcePoolConfig(this);
      }
   }
}
//使用
ResourcePoolConfig dbConfig = new Builder()
      .setName("DBConfig")
      .setMaxIdle(10)
      .setMaxIdle(12)
      .build();

使用建造者模式创建对象,还能避免对象存在无效状态问题

如果不适用建造者模式,先创建对象再set,会导致set第一个之后对象处于无效状态。为了避免这种状态存在,我们就需要一次性初始化好全部成员变量。如果构造函数参数过多我们就可以考虑建造者模式,但是使用建造者创建对象代码重复冗余。

与工厂模式区别

实际上,工厂模式是用来创建不同但是类型相关的对象(继承同一父类或者接口一组子类),由给定的参数来决定创建哪种类型对象。建造者是用来创建一种复杂对象,通过设置不同可选参数,‘定制化’建不同的对象。
实际上,我们最主要需要知道每个模式为什么这么设计,能解决什么问题,只有了解了这些最本质问题,才能不生搬硬套,才能灵活运用,甚至混用各种创造新模式,来解决特定问题。