一、定义
建造者模式(Builder) : 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式结构图,如下图1-1所示:
图 1-1
二、实例展示
Product类:产品类,由多个部件组成。
1 public class Product{ 2 Listparts = new ArrayList ();3 public void Add(String part){4 parts.Add(part);5 } 6 public void Show(){ 7 System.out.println("产品创建----"); 8 for(String part : parts){ 9 System.out.println(part); 10 } 11 }12}
Builder类:抽象建造者类,确定产品由两个部件PartA和PartB组成,并声明一个得到产品建造后结果的方法GetResult。
1 public abstract class Builder{2 public abstract void BuildPartA(); 3 public abstract void BuildPartB(); 4 public abstract Product GetResult();5 }
ConcreteBuilder1:具体建造者类。
1 public class ConcreteBuilder1 extends Builder{ 2 private Product product = new Product(); 3 @override4 public void BuildPartA(){5 product.Add("部件A");6 } 7 @Override 8 public void BuildPartB(){ 9 product.Add("部件B"); 10 } 11 @override 12 public void Product GetResult(){ 13 result product; 14 }15}
ConcreteBuilder2:具体建造者类。
1 public class ConcreteBuilder2 extends Builder{ 2 private Product product = new Product(); 3 @override4 public void BuildPartA(){5 product.Add("部件X");6 } 7 @override 8 public void BuildPartB(){ 9 product.Add("部件Y"); 10 } 11 @override 12 public Product GetResult(){ 13 return product; 14 }15}
Director类:指挥者类。
1 public class Director{2 public void Construct(Builder builder){3 builder.BuildPartA(); 4 builder.BuildPartB();5 }6 }
客户端代码,客户不知道具体的建造过程。
1 public static void Main(String[] args) {2 Director director = new Director(); 3 Builder b1 = new ConcreteBuilder1(); 4 Builder b2 = new ConcreteBuilder2(); 5 director.Construct(b1); 6 Product p1 = b1.GetResult(); 7 p1.Show(); 8 director.Construct(b2); 9 Product p2 = b2.GetResult(); 10 p2.Show(); 11}
三、使用场景
1、需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
2、需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
3、对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
4、隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
四、使用总结
1、建造者模式优点:
1) 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
2)每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合 "开闭原则"。
3)可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
2、建造者模式缺点:
1) 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
2)如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
五、典型应用
1、java.lang.StringBuilder 中的建造者模式
StringBuilder
的继承实现关系如下图 5-1 所示
图 5-1
Appendable
接口如下:
1 public interface Appendable { 2 Appendable append(CharSequence csq) throws IOException; 3 Appendable append(CharSequence csq, int start, int end) throws IOException; 4 Appendable append(char c) throws IOException; 5 }
StringBuilder
中的 append
方法使用了建造者模式,不过装配方法只有一个,并不算复杂,append
方法返回的是 StringBuilder
自身
1 public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { 2 @Override 3 public StringBuilder append(String str) { 4 super.append(str); 5 return this; 6 } 7 // ...省略... 8 }
StringBuilder
的父类 AbstractStringBuilder
实现了 Appendable
接口
1 abstract class AbstractStringBuilder implements Appendable, CharSequence { 2 char[] value; 3 int count; 4 public AbstractStringBuilder append(String str) { 5 if (str == null) 6 return appendNull(); 7 int len = str.length(); 8 ensureCapacityInternal(count + len); 9 str.getChars(0, len, value, count); 10 count += len; 11 return this; 12 } 13 private void ensureCapacityInternal(int minimumCapacity) { 14 // overflow-conscious code 15 if (minimumCapacity - value.length > 0) { 16 value = Arrays.copyOf(value,newCapacity(minimumCapacity)); 17 } 18 } 19 // ...省略... 20 }
这里可以看出,Appendable
为抽象建造者,定义了建造方法,StringBuilder
既充当指挥者角色,又充当产品角色,又充当具体建造者,建造方法的实现由 AbstractStringBuilder
完成,而 StringBuilder
继承了 AbstractStringBuilder。
2、myBatis 中的建造者模式
org.apache.ibatis.session
包下的 SqlSessionFactoryBuilder
类,如下图 5-2 所示:
图 5-2
里边很多重载的 build
方法,返回值都是 SqlSessionFactory
,除了最后两个所有的 build
最后都调用下面这个 build
方法:
1 public SqlSessionFactory build(Reader reader, String environment, Properties properties) { 2 SqlSessionFactory var5; 3 try { 4 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); 5 var5 = this.build(parser.parse()); 6 } catch (Exception var14) { 7 throw ExceptionFactory.wrapException("Error building SqlSession.", var14); 8 } finally { 9 ErrorContext.instance().reset(); 10 try { 11 reader.close(); 12 } catch (IOException var13) { 13 14 } 15 } 16 return var5; 17 }
其中最重要的是 XMLConfigBuilder
的 parse
方法,代码如下:
1 public class XMLConfigBuilder extends BaseBuilder { 2 public Configuration parse() { 3 if (this.parsed) { 4 throw new BuilderException("Each XMLConfigBuilder can only be used once."); 5 } else { 6 this.parsed = true; 7 this.parseConfiguration(this.parser.evalNode("/configuration")); 8 return this.configuration; 9 } 10 } 11 private void parseConfiguration(XNode root) { 12 try { 13 Properties settings = this.settingsAsPropertiess(root.evalNode("settings")); 14 this.propertiesElement(root.evalNode("properties")); 15 this.loadCustomVfs(settings); 16 this.typeAliasesElement(root.evalNode("typeAliases")); 17 this.pluginElement(root.evalNode("plugins")); 18 this.objectFactoryElement(root.evalNode("objectFactory")); 19 this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); 20 this.reflectorFactoryElement(root.evalNode("reflectorFactory")); 21 this.settingsElement(settings); 22 this.environmentsElement(root.evalNode("environments")); 23 this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); 24 this.typeHandlerElement(root.evalNode("typeHandlers")); 25 this.mapperElement(root.evalNode("mappers")); 26 } catch (Exception var3) { 27 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); 28 } 29 } 30 // ...省略... 31 }
parse
方法最终要返回一个 Configuration
对象,构建 Configuration
对象的建造过程都在 parseConfiguration
方法中,这也就是 Mybatis
解析 XML配置文件
来构建 Configuration
对象的主要过程
参考:
1、https://mp.weixin.qq.com/s?__biz=MzI1NDU0MTE1NA==&mid=2247483712&idx=1&sn=1ffd9837eb9413dde74ff509bf69ecc5&chksm=e9c2ed4ddeb5645b8cbf64c83d103a859ae49921c60e17fe8bebe63c26b04966be101d598848&scene=0#rd
2、大话设计模式。