建造者模式

1. 建造者模式简介

建造者模式用于将复杂对象的创建步骤分离开,根据需求,来生成不同的产品。建造者模式的类图如图所示:



建造模式的角色:

  • 建造者接口(Builder):用于定义产品对象各部分的行为。
  • 具体建造者(Concrete Builder):具体建造者类中有两类方法:一类用于建造各个部分,另一类用于组合各个部分,形成最终产品对象。
  • 导演(Director):该角色会调用具体建造者,创建需要的产品对象。
  • 产品(Product):产品就是用户最终需要的复杂对象。

2. MyBatis中的建造者模式

MyBatis在处理mybatis-config.xml和mapper映射文件时,涉及到的很多类都用到了建造者模式,如BaseBuilder及其子类、ResultMapping和ResultMapping.Builder、CacheBuilder、MappedStatement和MappedStatement.Builder等,都扮演着建造者模式中的某个角色。这些类采用了多种编码方式,下面对其中几种形式进行介绍,来理解建造者模式的不同组织形式:

2.1 形式一
2.1.1 具体建造者和产品
// CacheBuilder扮演着具体建造者的角色
// Cache是最终的产品
public class CacheBuilder {

  // 这些字段与mapper映射文件中<cache>节点的属性对应,
  // 在下面CacheBuilder.build方法中会用到这些属性
  private final String id;
  private Class<? extends Cache> implementation;
  private final List<Class<? extends Cache>> decorators;
  private Integer size;
  private Long clearInterval;
  private boolean readWrite;
  private Properties properties;
  private boolean blocking;
  
  // 这些方法用于建造产品的各个部分,注意返回值都是this
  public CacheBuilder implementation(Class<? extends Cache> implementation) {
    this.implementation = implementation;
    return this;
  }

  public CacheBuilder addDecorator(Class<? extends Cache> decorator) {
    if (decorator != null) {
      this.decorators.add(decorator);
    }
    return this;
  }

  public CacheBuilder size(Integer size) {
    this.size = size;
    return this;
  }

  public CacheBuilder clearInterval(Long clearInterval) {
    this.clearInterval = clearInterval;
    return this;
  }

  public CacheBuilder readWrite(boolean readWrite) {
    this.readWrite = readWrite;
    return this;
  }

  public CacheBuilder blocking(boolean blocking) {
    this.blocking = blocking;
    return this;
  }
  
  public CacheBuilder properties(Properties properties) {
    this.properties = properties;
    return this;
  }

  // 注意build的返回值是Cache(最终的产品对象),用于生成最终的产品对象
  public Cache build() {
    // 方法的大致内容就是利用<cache>节点中配置的属性创建Cache对象,
    // 方法具体内容不再详细介绍,重在理解建造者模式的形式
    setDefaultImplementations();
    // 通过反射创建底层Cache对象
    Cache cache = newBaseCacheInstance(implementation, id);
    setCacheProperties(cache);
    if (PerpetualCache.class.equals(cache.getClass())) {
      for (Class<? extends Cache> decorator : decorators) {
        cache = newCacheDecoratorInstance(decorator, cache);
        setCacheProperties(cache);
      }
      cache = setStandardDecorators(cache);
    } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
      cache = new LoggingCache(cache);
    }
    // 建造完毕,返回最终的Cache
    return cache;
  }
  
  // 其他(略)
  
}
2.1.2 导演
// MapperBuilderAssistant扮演着导演的角色
public class MapperBuilderAssistant extends BaseBuilder {
  
  // 创建最终的产品对象
  public Cache useNewCache(Class<? extends Cache> typeClass,
      Class<? extends Cache> evictionClass,
      Long flushInterval,
      Integer size,
      boolean readWrite,
      boolean blocking,
      Properties props) {
    // CacheBuilder是具体的建造者,Cache是最终产品
    Cache cache = new CacheBuilder(currentNamespace)
        .implementation(valueOrDefault(typeClass, PerpetualCache.class)) // 默认用PerpetualCache
        .addDecorator(valueOrDefault(evictionClass, LruCache.class)) // 默认用LruCache
        .clearInterval(flushInterval)
        .size(size)
        .readWrite(readWrite)
        .blocking(blocking)
        .properties(props)
        .build();
    // 无关代码(略)
    return cache;
  }
  
  // 其他(略)
  
}
2.2 形式二
2.2.1 产品和具体建造者
// ResultMap是最终的产品
public class ResultMap {
  
  private Configuration configuration;
  // 下面这些字段与mapper映射文件中<resultMap>的所有属性、子节点对应
  private String id; 
  private Class<?> type; 
  private List<ResultMapping> resultMappings;
  private List<ResultMapping> idResultMappings;
  private List<ResultMapping> constructorResultMappings;
  private List<ResultMapping> propertyResultMappings;
  private Set<String> mappedColumns;
  private Set<String> mappedProperties;
  private Discriminator discriminator;
  private boolean hasNestedResultMaps;
  private boolean hasNestedQueries;
  private Boolean autoMapping;

  // Builder是ResultMap的静态内部类,扮演着具体建造者的角色
  public static class Builder {
  
    // 创建空的ResultMap,Builder.build方法中会利用<resultMap>节点中的配置,对该空ResultMap进行"填充"
    private ResultMap resultMap = new ResultMap();  
    // 这里ResultMap的一部分字段在构造器中初始化
    public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings, Boolean autoMapping) {
      resultMap.configuration = configuration;
      resultMap.id = id;
      resultMap.type = type;
      resultMap.resultMappings = resultMappings;
      resultMap.autoMapping = autoMapping;
    }   

    // 处理<resultMap>的<discriminator>子节点,注意返回值为this
    public Builder discriminator(Discriminator discriminator) {
      resultMap.discriminator = discriminator;
      return this;
    }
  
    // 生成最终的产品对象(ResultMap)
    public ResultMap build() {
      // ... 创建过程的代码(略)
      return resultMap; // 建造完毕,返回最终的ResultMap
    }
    
    // 其他(略)
    
  }
    
  // 其他(略)

}
2.2.2 导演
// MapperBuilderAssistant扮演着导演的角色
public class MapperBuilderAssistant extends BaseBuilder {
  public ResultMap addResultMap(
      String id,  
      Class<?> type,
      String extend,
      Discriminator discriminator,
      List<ResultMapping> resultMappings,
      Boolean autoMapping) {
    
    // 创建ResultMap
    ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
        .discriminator(discriminator)
        .build();
        
    // 无关代码(略)
    
    return resultMap;
  }

  // 其他(略)

}
2.3 形式三

BaseBuilder及其子类的继承关系如图所示:



BaseBuilder的所有子类都扮演着具体建造者的角色,下面会以XMLConfigBuilder进行说明。

2.3.1 建造者接口和产品
// BaseBuilder与建造者接口角色对应(注意这里BaseBuilder是抽象类)
public abstract class BaseBuilder {

  // Configuration是MyBatis中全局唯一的配置对象,所有配置信息都封装在Configuration中
  // BaseBuilder所有子类构建的所有信息几乎都会记录在Configuration中,
  // 所以可以说Configuration是BaseBuilder所有子类共同生成的最终产品
  protected final Configuration configuration;

  // 除了Configuration,BaseBuilder就是普通的抽象父类,
  // 它封装了一些子类要用的方法,代码不再贴出

}
2.3.2 具体建造者
// 这里以XMLConfigBuilder为例进行介绍
// XMLConfigBuilder扮演着具体建造者的角色,主要负责解析mybatis-config.xml配置文件
public class XMLConfigBuilder extends BaseBuilder {

  // 解析mybatis-config.xml配置文件
  private void parseConfiguration(XNode root) {
    try {
      // 下面这些方法各自负责解析mybatis-config.xml的一部分,用于建造Configuration的各个部分
      // 会将mybatis-config.xml解析后的信息全部存入全局唯一的Configuration对象中
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings); 
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings); 
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 该方法中会处理<mappers>节点,并且可能会解析指定的mapper映射文件
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }  

  // 其余(略)

}
2.3.3 导演
// 可以说SqlSessionFactoryBuilder扮演着导演的角色
public class SqlSessionFactoryBuilder {

  // MyBatis的初始化入口
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    // try/catch(略)
    // 创建XMLConfigBuilder(具体建造者)
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    // 调用XMLConfigBuilder.parse()进行建造
    return build(parser.parse());
  }

  // 其余(略)

}

3. 总结

建造者模式将复杂对象的创建过程分散到不同的步骤中,实现了产品创建过程的精细控制。理论上,当需要新产品时,只需添加新的具体建造者,无需修改原有代码,符合开闭原则。
MyBatis的mapper映射文件中,各节点的属性较多,且很多都可有可无,因此解析mapper映射文件中节点的很多类都用了第二种形式的建造者模式,将创建的过程分离开,而不是全放在构造器中。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容