有时,一个对象会由不同的部件组成,一些情况下,在某些部件没有恰当的值之前,对象不能作为一个完整的产品使用,甚至有时候,一个对象的组成部件必须按照某个顺序赋值才有意义。建造者模式利用导演者对象以及具体建造者对象,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1. 结构
抽象建造者角色(Builder):给出各个部件的创建接口,以规范Product各个组成部件的建造,通常独立于应用的业务逻辑。
具体建造者角色(ConcreteBuilder):实现各个部件的具体构造和装配,与应用程序紧密相关。
导演者角色(Director):负责安排具体建造者创建。
产品角色(Product):建造的对象。
导演者将客户端创建产品的请求划分为对各个部件的建造请求,并将这些请求委派给具体建造者,客户端直接访问导演者而对建造过程不感知。它将产品本身与产品的创建过程分开,使得相同的创建过程可以创建不同的产品对象。因为每一个具体建造者都相对独立,因此可以很方便地替换或增加具体建造者。这样在有些情况下,如果将具体建造者再抽象出一层接口,就可以很灵活的改变建造过程了。
2. 应用 2.1. java StringBuilder StringBuilder
中的append
方法就使用了建造者模式,不过装配方法只有一个,append
返回自身,比较简单。
Appendable
为抽象建造者,定义了建造方法
java.lang.Appendable 1 2 3 4 5 public interface Appendable { Appendable append (CharSequence csq) throws IOException ; Appendable append (CharSequence csq, int start, int end) throws IOException ; Appendable append (char c) throws IOException ; }
StringBuilder
既充当导演者,也充当产品角色,还充当具体建造者,具体建造在AbstractStringBuilder
中实现
java.lang.AbstractStringBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 abstract class AbstractStringBuilder implements Appendable , CharSequence { char [] value; int count; public AbstractStringBuilder append (String str) { if (str == null ) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0 , len, value, count); count += len; return this ; } private void ensureCapacityInternal (int minimumCapacity) { if (minimumCapacity - value.length > 0 ) { value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } }
另外,StringBuilder
还有一个兄弟实现类StringBuffer
,它们的区别只在于是否使用了同步
java.lang.StringBuilder 1 2 3 4 5 @Override public StringBuilder append (String str) { super .append(str); return this ; }
java.lang.StringBuffer 1 2 3 4 5 6 @Override public synchronized StringBuffer append (String str) { toStringCache = null ; super .append(str); return this ; }
2.2. mybatis SqlSessionFactoryBuilder org.apache.ibatis.session.SqlSessionFactoryBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SqlSessionFactoryBuilder { 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) { } } } public SqlSessionFactory build (Configuration config) { return new DefaultSqlSessionFactory(config); } }
从命名可以看出SqlSessionFactoryBuilder
想要构建一个SqlSessionFactory
,不过首先它需要先构建一个Configuration
,而Configuration
由XMLConfigBuilder
负责构建
org.apache.ibatis.builder.xml.XMLConfigBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class XMLConfigBuilder extends BaseBuilder { public Configuration parse () { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once." ); } parsed = true ; parseConfiguration(parser.evalNode("/configuration" )); return configuration; } private void parseConfiguration (XNode root) { try { 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" )); mapperElement(root.evalNode("mappers" )); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } }
parse
方法最终返回一个Configuration
,构建Configuration
对象的建造过程都在parseConfiguration
方法中,这也就是 Mybatis解析XML配置文件的主要过程了。
参考:
《java与模式》