设计模式之创建型模式中的建造者模式(Builder Pattern)

Builder Pattern

基本介绍

在java开发过程中,我们经常会创建大量的对象,这些对象有简单的也有复杂的,但是不管是简单对象还是复杂对象,构建这些对象的过程是相对稳定的,只不过它们的内部属性(成员属性)不同,这也就意味着构建的具体内部细节不一样。那么,基于这种情况,我们可以将复杂的对象的构建过程抽象出来,通过抽象过程的不同实现方法来实现不同对象的内部细节构建过程,从某种意义上来说,这也是产品构建过程复杂度的解耦,这就是建造者模式(Builder Pattern)。
建造者模式(Builder Pattern)又叫生成器模式,它把对象的创建步骤抽象成生成器,将一个产品的内部表象与产品的生产过程分割开来,一步一步构建一个复杂的对象,用户只用指定复杂对象的类型和内容,而无需知道内部的具体构建细节就可以构建它们。

建造者模式的四个角色

  • 产品角色(Product):我们所要构建的产品对象;
  • 抽象建造者(Builder):创建产品(Product)对象的接口/抽象类。它定义了创建一个产品(Product)对象所需要的各个部件的操作(抽象方法),同时包含一个获取产品(Product)对象(获取成品)的方法。
  • 具体建造者(ConcreteBuilder):抽象建造者的具体实现,实现抽象建造者(Builder)的接口,构建和装配产品(Product)对象的各个部件,对于不同的部件或者构建步骤进行不同的详细实现,来完成不同的产品。
  • 指导者(Director):主要用来使用Buider接口,构建一个使用Buider接口的对象,以一个相对稳定且统一的过程生产产品(Product)对象。

日常举例

建造小汽车的例子:小汽车主要部件:发动机(Engine)、车身框架(Frame)、轮胎(Wheel)等,不管是什么品牌的汽车,都有这些部件,只不过内部的构造和质量等不一样。代码实现,如下:

  • 产品角色(Product)
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
36
37
public class Car {
private String engine;
private String frame;
private String wheel;

public String getEngine() {
return engine;
}

public void setEngine(String engine) {
this.engine = engine;
}

public String getFrame() {
return frame;
}

public void setFrame(String frame) {
this.frame = frame;
}

public String getWheel() {
return wheel;
}

public void setWheel(String wheel) {
this.wheel = wheel;
}
@Override
public String toString() {
return "Car{" +
"engine='" + engine + '\'' +
", frame='" + frame + '\'' +
", wheel='" + wheel + '\'' +
'}';
}
}
  • 抽象建造者(Builder)
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
public interface CarBuilder {
/**
* 建造发动机
* @return
*/
String buildEngine();

/**
* 建造车身框架
* @return
*/
String buildFrame();

/**
* 建造轮胎
* @return
*/
String buildWheel();

/**
* 获取小汽车成品
* @return
*/
Car getCar();
}
  • 具体建造者(ConcreteBuilder)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class BaoMaCarBuilder implements CarBuilder{
Car car = new Car();
public void buildEngine() {
this.car.setEngine("建造宝马发动机!");
}

public void buildFrame() {
this.car.setFrame("建造宝马车身框架!");
}
public void buildWheel() {
this.car.setWheel("建造宝马轮胎!");
}

public Car getCar() {
return this.car;
}
}
  • 指导者(Director)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CarDirector {
private CarBuilder carBuilder;

public CarDirector(CarBuilder carBuilder) {
this.carBuilder = carBuilder;
}

/**
* 构建汽车的流程交给指导者
* @return
*/
public Car builderCar(){
carBuilder.buildEngine();
carBuilder.buildFrame();
carBuilder.buildWheel();
return carBuilder.getCar();
}
}
  • 客户端使用
1
2
3
4
5
6
7
8
public class Client {
public static void main(String[] args) {
BaoMaCarBuilder baoMaCarBuilder = new BaoMaCarBuilder();
CarDirector carDirector = new CarDirector(baoMaCarBuilder);
Car car = carDirector.builderCar();
System.out.println(car);
}
}

运行结果:
客户端运行结果

建造者模式在JDK中的应用

先看一张StringBuilder类图
StringBuilder类图
源码:java.lang.Appendable

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public interface Appendable {

/**
* Appends the specified character sequence to this <tt>Appendable</tt>.
*
* <p> Depending on which class implements the character sequence
* <tt>csq</tt>, the entire sequence may not be appended. For
* instance, if <tt>csq</tt> is a {@link java.nio.CharBuffer} then
* the subsequence to append is defined by the buffer's position and limit.
*
* @param csq
* The character sequence to append. If <tt>csq</tt> is
* <tt>null</tt>, then the four characters <tt>"null"</tt> are
* appended to this Appendable.
*
* @return A reference to this <tt>Appendable</tt>
*
* @throws IOException
* If an I/O error occurs
*/
Appendable append(CharSequence csq) throws IOException;

/**
* Appends a subsequence of the specified character sequence to this
* <tt>Appendable</tt>.
*
* <p> An invocation of this method of the form <tt>out.append(csq, start,
* end)</tt> when <tt>csq</tt> is not <tt>null</tt>, behaves in
* exactly the same way as the invocation
*
* <pre>
* out.append(csq.subSequence(start, end)) </pre>
*
* @param csq
* The character sequence from which a subsequence will be
* appended. If <tt>csq</tt> is <tt>null</tt>, then characters
* will be appended as if <tt>csq</tt> contained the four
* characters <tt>"null"</tt>.
*
* @param start
* The index of the first character in the subsequence
*
* @param end
* The index of the character following the last character in the
* subsequence
*
* @return A reference to this <tt>Appendable</tt>
*
* @throws IndexOutOfBoundsException
* If <tt>start</tt> or <tt>end</tt> are negative, <tt>start</tt>
* is greater than <tt>end</tt>, or <tt>end</tt> is greater than
* <tt>csq.length()</tt>
*
* @throws IOException
* If an I/O error occurs
*/
Appendable append(CharSequence csq, int start, int end) throws IOException;

/**
* Appends the specified character to this <tt>Appendable</tt>.
*
* @param c
* The character to append
*
* @return A reference to this <tt>Appendable</tt>
*
* @throws IOException
* If an I/O error occurs
*/
Appendable append(char c) throws IOException;
}

源码: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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
abstract class AbstractStringBuilder implements Appendable, CharSequence {
...
// Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}

/**
* @since 1.8
*/
AbstractStringBuilder append(AbstractStringBuilder asb) {
if (asb == null)
return appendNull();
int len = asb.length();
ensureCapacityInternal(count + len);
asb.getChars(0, len, value, count);
count += len;
return this;
}

// Documentation in subclasses because of synchro difference
@Override
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);

return this.append(s, 0, s.length());
}
...
}

源码:java.lang.StringBuilder(片段)

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
36
37
38
39
40
41
42
43
44
45
46
47
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
...
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}

@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}

/**
* Appends the specified {@code StringBuffer} to this sequence.
* <p>
* The characters of the {@code StringBuffer} argument are appended,
* in order, to this sequence, increasing the
* length of this sequence by the length of the argument.
* If {@code sb} is {@code null}, then the four characters
* {@code "null"} are appended to this sequence.
* <p>
* Let <i>n</i> be the length of this character sequence just prior to
* execution of the {@code append} method. Then the character at index
* <i>k</i> in the new character sequence is equal to the character at
* index <i>k</i> in the old character sequence, if <i>k</i> is less than
* <i>n</i>; otherwise, it is equal to the character at index <i>k-n</i>
* in the argument {@code sb}.
*
* @param sb the {@code StringBuffer} to append.
* @return a reference to this object.
*/
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}

@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
...
}

分析: 接口Appendable定义了多个append抽象方法,即为抽象建造者;抽象类AbstractStringBuilder实现了接口Appendable的方法,即为具体建造者,但是不能实例化;StringBuilder继承了抽象类AbstractStringBuilder,具体的方法已经由AbstractStringBuilder实现,StringBuilder对部分方法进行了覆盖,由此可以看出,StringBuilder既充当了具体建造者,也是指导者的角色。

附:本次演示的项目地址
https://github.com/syshlang/java-design-patterns