前言
主要内容有:
- 该设计模式的详细介绍,包括:
- 引子,意图(大白话解释)
- 类图,时序图(理论规范)
- 该模式的代码示例:熟悉它长的样子
- 该模式的实际使用案例:了解它在哪些重要的源码中出现过
创建型:建造者模式 Builder
引子
根据中文翻译的不同,建造者模式又可以称为生成器模式,它属于创建型模式。
复杂对象相当于一辆有待建造的汽车,而对象的属性相当于汽车的部件,建造产品的过程就相当于组合部件的过程。由于组合部件的过程很复杂,因此,这些部件的组合过程往往被“外部化”到一个称作建造者的对象里,建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式,这就是建造者模式的模式动机。
我们常用的StringBuilder就是一个建造者模式的典型例子
意图
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
类图
如果看不懂UML类图,可以先粗略浏览下该图,想深入了解的话,可以继续谷歌,深入学习:
建造者模式包含如下角色:
- Builder:抽象建造者 (我们经常会看到AbstractXXXX类)
- ConcreteBuilder:具体建造者
- Director:指挥者
- Product:产品角色
下图是建造者模式得到UML图,可以看到:
- ConcreteBuilder是Builder的实现关系
- Builder聚合到Director上,因为Director还可以干别的事情
时序图
时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。
我们可以大致浏览下时序图,如果感兴趣的小伙伴可以去深究一下:
实现
1. 建造一个水杯
一水杯工厂要生产各式各样的水杯,无论杯子是神马造型,但都包括绳子,帽子和杯体。以此模型创建各种类型的杯子。
cup-->Product类
public class Cup { private String string; //绳子 private String cap; //帽子 private String cupBody; //杯体 public void setString(String string) { this.string = string; } public void setCap(String cap) { this.cap = cap; } public void setCupBody(String cupBody) { this.cupBody = cupBody; } public void show() { System.out.println("杯子生产完毕"); } } 复制代码
Builder-->Builder类
public abstract class Builder { protected Cup cup = new Cup(); public abstract void buildString(); public abstract void buildCap(); public abstract void buildCupBody(); public Cup getResult() { return cup; } } 复制代码
ClassCup-->concrateBuilder类
public class ClassCup extends Builder { @Override public void buildString() { cup.setString("生产绳子..."); System.out.println("生产绳子..."); } @Override public void buildCap() { cup.setCap("生产帽子..."); System.out.println("生产帽子..."); } @Override public void buildCupBody() { cup.setCupBody("生产杯体..."); System.out.println("生产杯体..."); } } 复制代码
Director类
public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public void create() { builder.buildString(); builder.buildCap(); builder.buildCupBody(); builder.getResult().show(); } public static void main(String[] args) { Director d = new Director(new ClassCup()); d.create(); } } 复制代码
2. 编写一个简易的java.lang.StringBuilder
AbstractStringBuilder-->Builder类
public class AbstractStringBuilder { protected char[] value; protected int count; public AbstractStringBuilder(int capacity) { count = 0; value = new char[capacity]; } public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); value[count++] = c; return this; } private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); } void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); } } 复制代码
StringBuilder-->concrateBuilder类
public class StringBuilder extends AbstractStringBuilder { public StringBuilder() { super(16); } @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); } } 复制代码
Client-->Director类
public class Client { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); final int count = 26; for (int i = 0; i < count; i++) { sb.append((char) ('a' + i)); } System.out.println(sb.toString()); } } 复制代码
优缺点
优点
- 在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
- 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
使用场景举例
JDK中使用
- java.lang.StringBuilder
- java.nio.ByteBuffer
- java.lang.StringBuffer
- java.lang.Appendable
- Apache Camel builders