设计模式 笔记8 | 建造者模式 在源码中的应用 StringBuilder 的 append | MyBatis 的 SqlSessionFactoryBuilder 和 ConfigBuilder

文章目录

  • 一、建造者模式知识回顾
  • 二、StringBuilder 中的建造者模式
  • 三、MyBatis 中的建造者模式
  • 四、总结

一、建造者模式知识回顾


建造者模式是指将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。
优点:

  • 封装性好,构建和表示分离
  • 扩展性好,各个具体的建造者相互独立,利于解耦合

缺点:

  • 产品的组成部分必须相同,限制了使用范围
  • 当复杂的产品内部发生变化时,建造者也需同步修改,后期的维护成本大

建造者和工厂模式的区别:

  • 建造者更注重产品零部件的组装过程,而工厂方法模式更注重零部件的创建过程

( 建造者模式和工厂方法模式可结合使用。)

主要组成:
1)产品角色(Product),包含多个组成部件的复杂对象,由建造者来创建其各个零件
2)抽象建造者(Builder),一个包含产品各个子部件的抽象方法的接口,通常还包含返回复杂产品的方法,build()
3)具体建造者(Concrete Builder),实现 Builder 接口,完成复杂产品的各个部件的具体创建方法
4)指挥者(Driver),调用建造者对象中的部件构造与状态方法完成复杂对象的创建,简单理解就是创建对象的类。

模板代码:( 直接使用静态内部类实现 具体建造者,其中抽象建造者不用也行)

class People{
    private String name;
    private Integer age;

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
    private People(){}
    private People(String name, Integer age){
        this.name = name;
        this.age = age;
    }
    public static class Builder{
        private String name;
        private Integer age;
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        public Builder age(Integer age) {
            this.age = age;
            return this;
        }
        public People build(){
            return new People(name, age);
        }
    }
}

使用:

class Demo{
    public static void main(String[] args) {
        People uni = new People.Builder()
                .name("uni")
                .age(22)
                .build();
        System.out.println(uni);
    }
}

适用场景:

  • 相同方法,不同执行顺序,产生不同的结果
  • 多个成员属性,都可以装配到一个对象,但是产生结果不同
  • 产品类复杂,或者产品类中不同的调用顺序会产生不同的作用
  • 初始化一个对象特别复杂,参数多,而且很多参数都有默认的值

二、StringBuilder 中的建造者模式


JDK 的 StringBuilder 类中提供了 append() 追加方法,这是一种链式创建对象的方法,开放构造步骤,最后调用了 toString() 方法就可获得完整的对象,例如:

String s = new StringBuilder()
    .append("hello, ")
    .append("Wolrd!")
    .toString();

StringBuilder 部分源码如下:

public final class StringBuilder
    extends AbstractStringBuilder
    implements Serializable, CharSequence
{
    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
    ...
    @Override
    public String toString() {
        // 创建拷贝对象, 不分享内部的字符数组
        return new String(value, 0, count);
    }
}

StringBuilder 类内部存储字符串的结构为 char[] value;
是存储在父类,即抽象类 AbstractStringBuilder 中的,在StringBuillder 无参构造中,设置了字符数组默认长度为 16,当添加的字符串长度超过这个值时,其内部会有扩容机制

public StringBuilder() {
    super(16);
}

扩容机制在抽象类 AbstractStringBuilder中的ensureCapacityInternal方法实现:

private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                              newCapacity(minimumCapacity));
    }
}

我们可通过调试代码来观察其底层的扩容机制,因为底层默认长度是16,我们试试刚好append到17个字符串长度,这样其底层会进行扩容

String s = new StringBuilder()
    .append("1234567890")
    .append("1234567")
    .toString();

调试过程如下图:

三、MyBatis 中的建造者模式


mybatis 的 SqlSessionFactoryBuilder 类 用到了建造者模式,而且在 Mybatis中 SqlSessionFactory是由SqlSessionFactoryBuilder产生的,代码如下:

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

DefaultSqlSessionFactory 类的有参构造需传入 Mybatis 核心配置类 Configuration 的对象作为参数,而 Configuration 内容很多,初始化比较麻烦,因此使用了专门的建造者 XMLConfigBuilder 进行创建。
在 SqlSessionFactoryBuilder 类中有几个重载的 build 方法,其中最主要的实现如下:

public class SqlSessionFactoryBuilder {
    
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 创建建造者 XMLConfigBuilder的 实例
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // XMLConfigBuilder 的 parse() 创建 Configuration 实例
            return build(parser.parse());
        } catch (Exception e) {
            // 捕获异常
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            // 重置错误
            ErrorContext.instance().reset();
            try {
                // 释放资源
                inputStream.close();
            } catch (IOException e) {
                // IO 错误不进行处理
            }
        }
    }
}

XMLConfigBuilder 负责 Configuration 各个组建的创建和装配,整个装配流程如下:

package org.apache.ibatis.builder.xml;
...

public class XMLConfigBuilder extends BaseBuilder {
    // 解析配置累
    private void parseConfiguration(XNode root) {
        try {
          // issue #117 read properties first
          // 1.读取XML的标签
          propertiesElement(root.evalNode("properties"));
          // 2. 读取XML的标签
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          // 3. 加载自定义的配置
          loadCustomVfs(settings);
          loadCustomLogImpl(settings);
          // 4. 加载XML的 标签
          typeAliasesElement(root.evalNode("typeAliases"));
          // 5. 加载XML的  标签
          pluginElement(root.evalNode("plugins"));
          // 6. 加载XML的 标签
          objectFactoryElement(root.evalNode("objectFactory"));
          // 7. 加载XML的标签
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          // 8. 加载XML的 标签
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          // 9. 更新设置
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          // 10. 加载XML的 标签
          environmentsElement(root.evalNode("environments"));
          // 11. 加载XML的  标签
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          // 12. 加载XML的  标签
          typeHandlerElement(root.evalNode("typeHandlers"));
          // 13. 加载XML的  标签
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
     }
}

XMLConfigBuilder 类负责创建复杂对象 Configuration,可以视为具体建造者角色,而SqlSessionFactoryBuilder则是以封装式构建 SqlSessionFactory 实例,相当于简化的建造者模式。

四、总结


本次学习的建造者模式分别在JDK源码的StringBuilder的 append()方法 、toString()方法 和 Mybatis源码的 SqlSessionFactoryBuilder 和 配置文件XMLConfigBuilder。

你可能感兴趣的:(设计模式,mybatis,设计模式,建造者模式)