◉◡◉ 您好,欢迎到访伊成个人站!

java设计模式之建造者模式

建造者模式介绍

建造者模式,顾名思义的就是类似建房子,有一个固定的流程。在大话设计模式中,有一个例子大概意思是同一道菜在中国的每一个地方都有不同的味道,而肯德基的鸡腿、汉堡在每一个城市都是一样的味道。我觉的这一个例子可以清楚的认识到建造者模式有一个固定的建造过程。建造者模式实现了依赖倒转原则,抽象不应该依赖细节,细节应该依赖与抽象。

建造者模式的定义是:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。

建造者模式UML类图

“伊成博客”

模式结构

Builder: 抽象建造者角色。为创建一个产品对象的各个部件指定抽象接口。

ConcreteBuilder:具体建造者角色。实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。

Director:指挥者角色。该角色负责调用具体建造者按照顺序建造产品。只负责调度,真正执行的是具体建造者角色。

Product:产品角色。该角色是建造的复杂对象,提供基本方法

举个栗子

假如现在要造两辆豪车,一辆是兰博基尼,一辆是法拉利。那怎么用代码实现造这两辆豪车呢?
首先看产品角色代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Production {
private String part1;
private String part2;

public String getPart1() {
return part1;
}

public void setPart1(String part1) {
this.part1 = part1;
}

public String getPart2() {
return part2;
}

public void setPart2(String part2) {
this.part2 = part2;
}
}

抽象建造者角色代码如下:

1
2
3
4
5
6
7
8
9
10
11
public interface IBuilder {

// 产品有多少个组件,就有多少个建造方法
public void buildPart1();

public void buildPart2();

// 返回产品类
public Production build();

}

开始造兰博基尼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 public class BuilderA implements IBuilder {

private Production production = new Production();

@Override
public void buildPart1() {
System.out.println("构造兰博基尼的第一部分。");
production.setPart1("This is part1 of Lamborghini");
}

@Override
public void buildPart2() {
System.out.println("构造兰博基尼的第二部分。");
production.setPart2("This is part2 of Lamborghini");
}

@Override
public Production build() {
System.out.println("兰博基尼已造好!");
return production;
}
}

开始造法拉利:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class BuilderB implements IBuilder {

private Production production = new Production();

@Override
public void buildPart1() {
System.out.println("构造法拉利的第一部分。");
production.setPart1("This is part1 of Ferrari");
}

@Override
public void buildPart2() {
System.out.println("构造法拉利的第二部分。");
production.setPart2("This is part2 of Ferrari");
}

@Override
public Production build() {
System.out.println("法拉利已造好!");
return production;
}
}

指挥者调度构建,返回对应产品。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Director {

private IBuilder builder;

public Director(IBuilder builder){
this.builder = builder;
}

/**
* 构造顺序
*/
public Production construct(){
builder.buildPart1();
builder.buildPart2();
return builder.build();
}

}

客户端调用一下,查看输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Client {

public static void main(String[] args) {

// 兰博基尼
IBuilder builderA = new BuilderA();
Director directorA = new Director(builderA);
directorA.construct();

// 法拉利
IBuilder builderB = new BuilderB();
Director directorB = new Director(builderB);
directorB.construct();
}

}

输出结果如下:

1
2
3
4
5
6
7
构造兰博基尼的第一部分。
构造兰博基尼的第二部分。
兰博基尼已造好!

构造法拉利的第一部分。
构造法拉利的第二部分。
法拉利已造好!

建造者模式的优缺点

优点:
1.封装性好,创建和使用分离
2.扩展性好,建造类之间独立,一定程度上实现了解耦

缺点:
1.产生多余的Builder对象
2.产品内部发生变化时,建造者都需要修改,成本较大

建造者模式使用案例

1.Spring框架中使用建造者模式

Spring中的UriComponents和UriComponentsBuilder

UriComponents基本方法

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
public abstract class UriComponents implements Serializable {
private static final String DEFAULT_ENCODING = "UTF-8";
// 用于分割uri的正则表达式,下面会说到
private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
private final String scheme;
private final String fragment;

protected UriComponents(String scheme, String fragment) {
this.scheme = scheme;
this.fragment = fragment;
}

// 多个Components对应的getter方法
/**
* 返回URL的scheme.
*/
public final String getScheme() {
return this.scheme;
}

/**
* 返回URL的fragment.
*/
public final String getFragment() {
return this.fragment;
}

/**
* 返回URL的schemeSpecificPar
*/
public abstract String getSchemeSpecificPart();

/**
* 返回userInfo
*/
public abstract String getUserInfo();

/**
* 返回URL的host
*/
public abstract String getHost();

/**
* 返回URL的port
*/
public abstract int getPort();

/**
* 返回URL的path
*/
public abstract String getPath();

/**
* 返回URL的path部分的集合
*/
public abstract List<String> getPathSegments();

/**
* 返回URL的query部分
*/
public abstract String getQuery();

/**
* 返回URL的query参数map
*/
public abstract MultiValueMap<String, String> getQueryParams();

/**
* 将URL的components用特定的编码规则编码并返回,默认为utf-8
*/
public final UriComponents encode() {
try {
return encode(DEFAULT_ENCODING);
}
catch (UnsupportedEncodingException ex) {
// should not occur
throw new IllegalStateException(ex);
}
}

/**
* 编码的抽象方法,传入相应的编码规则
*/
public abstract UriComponents encode(String encoding) throws UnsupportedEncodingException;

/**
* 将URL中的模板参数换成对应的值
*/
public final UriComponents expand(Map<String, ?> uriVariables) {
Assert.notNull(uriVariables, "'uriVariables' must not be null");
return expandInternal(new MapTemplateVariables(uriVariables));
}

/**
* 将URL中的模板参数换成对应的值,输入为数组
*/
public final UriComponents expand(Object... uriVariableValues) {
Assert.notNull(uriVariableValues, "'uriVariableValues' must not be null");
return expandInternal(new VarArgsTemplateVariables(uriVariableValues));
}

/**
* 将URL中的模板参数换成对应的值,输入为UriTemplateVariables
*/
public final UriComponents expand(UriTemplateVariables uriVariables) {
Assert.notNull(uriVariables, "'uriVariables' must not be null");
return expandInternal(uriVariables);
}

/**
* 将URL中的模板参数换成对应的值的最终的实现方法
*/
abstract UriComponents expandInternal(UriTemplateVariables uriVariables);

/**
* 处理URL
*/
public abstract UriComponents normalize();

/**
* 返回URL的string
*/
public abstract String toUriString();

/**
* 返回URI格式的方法
*/
public abstract URI toUri();

@Override
public final String toString() {
return toUriString();
}

/**
* 将这些Components的值赋给其builder类
*/
protected abstract void copyToUriComponentsBuilder(UriComponentsBuilder builder);
//……
}

UriComponentsBuilder类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 默认构造方法,其中path的构造类为CompositePathComponentBuilder,它为UriComponentsBuilder的内部静态类,主要实现对url的path部分进行构造。
*/
protected UriComponentsBuilder() {
this.pathBuilder = new CompositePathComponentBuilder();
}

/**
* 创建一个传入UriComponentsBuilder类的深拷贝对象
*/
protected UriComponentsBuilder(UriComponentsBuilder other) {
this.scheme = other.scheme;
this.ssp = other.ssp;
this.userInfo = other.userInfo;
this.host = other.host;
this.port = other.port;
this.pathBuilder = other.pathBuilder.cloneBuilder();
this.queryParams.putAll(other.queryParams);
this.fragment = other.fragment;
}

2.JDK中使用建造者模式

JDK中的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
public StringBuilder append(boolean b) {
super.append(b);
return this;
}

public StringBuilder append(char c) {
super.append(c);
return this;
}

public StringBuilder append(int i) {
super.append(i);
return this;
}

public StringBuilder append(long lng) {
super.append(lng);
return this;
}

public StringBuilder append(float f) {
super.append(f);
return this;
}

public StringBuilder append(double d) {
super.append(d);
return this;
}

“伊成博客”

由上图我们可以看出StringBuilder继承了AbstractStringBuilder,而AbstractStringBuilder实现了appendable。

StringBuilder:指挥者类,持有具体建造者的引用,由于StringBuilder继承了AbstractStringBuilder,这里StringBuilder通过super来作为具体建造者的引用。
AbstractStringBuilder:具体建造者,它实现了appendable接口的append(Character c)方法。
appendable:抽象建造者,定义了创建对象的接口。
StringBuilder的append(Character c)方法:

tips:
基本上看到源码中以 Builder 结尾的类,大多数都属于 建造者模式~


The end.

支付宝打赏 微信打赏