建造者模式

建造者模式 (builder pattern), 也被称为生成器模式 , 是一种创建型设计模式. 将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

比如: 一辆汽车是由多个部件组成的,包括了车轮、方向盘、发动机等等.对于大多数用户而言,并不需要知道这些部件的装配细节,并且几乎不会使用单独某个部件,而是使用一辆完整的汽车.而建造者模式就是负责将这些部件进行组装让后将完整的汽车返回给用户.

与工厂模式的区别:

  1. 工厂模式是一个接口创建不同类型的对象,由给定的参数确定是哪种类型

  2. 建造者模式是创建一个类型的对象,对象使用Builder来定制化的指定一些参数值

  3. 举例:餐馆工厂生产不同的食品,比如披萨、汉堡、可乐。但是披萨建造者使用不同的配料和火候建造不同种类的匹萨。

建造者(Builder)模式包含以下4个角色 :

  • 抽象建造者类(Builder):这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的部件对象的创建。

  • 具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供一个方法,返回创建好的负责产品对象。

  • 产品类(Product):要创建的复杂对象 (包含多个组成部件).

  • 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建(客户端一般只需要与指挥者进行交互)。

Java 创建同一类型的不同属性的对象

  • Bike是产品,包含车架、车座等组件

  • Builder是抽象构建造者

  • MobikeBuilder和HelloBuilder是具体的建造之

  • Director是指挥者

通过Builder接口,可以定制化自行车的实现,给自行车实例分类创建,从而减少创建步骤,创建出的自行车都是同一类型 Bike,但是他们的字段内容不同。这种方式个人理解为创建代码的模板化。

@Data
public class Bike {
    // 车架
    private Frame frame;
    // 车座
    private Seat seat;
}

public abstract class Builder {

    protected Bike mBike = new Bike();

    public abstract void buildFrame();

    public abstract void buildSeat(); 

    public abstract Bike buildBike();
}

public class MobikeBuilder extends Builder {
    
    @Override
    public void buildFrame() {
        this.mBike.setFrame(new Frame(xxx));
    }

    @Override
    public void buildSeat() {
        this.mBike.setSeat(new Seat(xxx));
    }

    @Override   
    public Bike buildBike(){
        // 校验
        return this;
    }

}

public class Director {

    private Builder mBuilder;

    public Director(Builder builder) {
        this.mBuilder = builder;
    }

    public Bike construct() {
        mBuilder.buildFrame();
        mBuilder.buildSeat();
        return mBuilder.buildBike();
    }

}

public static void main(String[] args) {
    Director director = mew Director(new MobikeBuilder());
    Bike bike = director.construct();
}

Java 解决复杂对象创建问题

主要用于解决复杂对象的创建问题:

  1. 使用全参构造创建复杂对象,构造参数过多,部分构造参数无法做成可选参数,且调用时很容易搞错参数顺序,影响代码可读性,导致发生隐蔽的bug。

  2. 使用无参构造+setter的方式创建对象,对象设置属性更加灵活,但是对象在创建后存在中间状态,并且针对校验setter有调用顺序约束,代码上很难做完整的校验。

故使用Builder方式来解决此类问题:

  1. 目标类构造方法要传入一个Builder对象

  2. Builder类要位于目标类的内部,且static修饰

  3. Builder类需要提供内置的各种setter,且返回值为Builder对象本身,以支持链式调用

  4. Builder类提供一个build()方法,用于创建并返回目标类

public class RabbitMQClient {

    // 私有化构造器,让该对象只能通过Builder来构造
    private RabbitMQ3(Builder builder) {

    }

    public static class Builder {
        // 保证属性的密闭性
        private String host = "127.0.0.1";
        private int port = 5672;
        private int mode;
        private String exchange;
        private String queue;
        private boolean isDurable = true;

        public Builder host(String host) {
            this.host = host;
            return this;
        }

        public Builder port(int port) {
            this.port = port;
            return this;
        }

        public Builder mode(int mode) {
            this.mode = mode;
            return this;
        }

        public Builder exchange(String exchange) {
            this.exchange = exchange;
            return this;
        }

        public Builder queue(String queue) {
            this.queue = queue;
            return this;
        }

        public Builder isDurable(boolean isDurable) {
            this.isDurable = isDurable;
            return this;
        }

        public RabbitMQClient build() {
            // 校验逻辑
            // ......
            return new RabbitMQClient(this);
        }
    }

}

Mybatis SqlSessionFactoryBuilder

用用构建SqlSessionFactory对象的建造器,提供了很多种类的build()方法,用于Client很方便的创建SqlSessionFactory实例:

public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

类似的还有 XMLConfigBuilderXMLMapperBuilder,在他们的内部会有大量的创建逻辑,是极其复杂的。

最后更新于