syshlang设计模式之创建型模式中的建造者模式(Builder Pattern) | 浪迹一生

所谓拥有,皆非束缚;所有过往,皆为序章。

0%

设计模式之创建型模式中的建造者模式(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类图

源码: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

------------- The End -------------
  • 本文作者: 浪迹一生
  • 本文链接: https://www.syshlang.com/8cf99788/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!