建造者模式是指将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。
优点:
缺点:
建造者和工厂模式的区别:
( 建造者模式和工厂方法模式可结合使用。)
主要组成:
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);
}
}
适用场景:
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 的 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。