基础加固-建造者模式

   上篇我们复习了工厂模式,这节我们来看一下创建型设计模式的另一种模式–建造者模式。

一.定义

   建造者模式是指将一个复杂对象的构建和它的表示分离,使同样的构建过程可以构建不同的表示。用户只需要指定需要建造的类型就可以得到它们,建造过程以及细节并不需要知道。适用于那些创建流程固定,但顺序不一定固定的对象。如果一个对象有非常复杂的内部结构,即有很多属性,我们想把这种复杂对象的创建和使用进行分离,我们可以使用建造者模式。这样看定义难免抽象,接下来我们一起编写代码来加深对工厂模式的理解。

二、标准版

1.代码实现

   首先我们有一个类Game,有名称、简介、制作团队、宣传视频、社区几个属性。代码如下:

Game.java
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
public class Game {
private String name;
private String introduce;
private String team;
private String video;
private String community;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getIntroduce() {
return introduce;
}

public void setIntroduce(String introduce) {
this.introduce = introduce;
}

public String getTeam() {
return team;
}

public void setTeam(String team) {
this.team = team;
}

public String getVideo() {
return video;
}

public void setVideo(String video) {
this.video = video;
}

public String getCommunity() {
return community;
}

public void setCommunity(String community) {
this.community = community;
}

@Override
public String toString() {
return "Game{" +
"name='" + name + '\'' +
", introduce='" + introduce + '\'' +
", team='" + team + '\'' +
", video='" + video + '\'' +
", community='" + community + '\'' +
'}';
}
}

我们创建一个抽象类GameBuilder,其中定义了创建各个属性的方法和生成Game对象的方法,代码如下:

GameBuilder.java
1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class GameBuilder {
public abstract void buildName(String name);

public abstract void buildIntroduce(String introduce);

public abstract void buildTeam(String team);

public abstract void buildVideo(String video);

public abstract void buildCommunity(String community);

public abstract Game makeGame();
}

接下来我们要创建具体的builder实现,比如我们要创建一个动作游戏的建造者ActionGameBuilder,我们只需要继承上面的抽象builder,重写里面的方法即可,代码如下:

ActionGameBuilder.java
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
public class ActionGameBuilder extends GameBuilder {
Game game = new Game();

@Override
public void buildName(String name) {
game.setName(name);
}

@Override
public void buildIntroduce(String introduce) {
game.setIntroduce(introduce);
}

@Override
public void buildTeam(String team) {
game.setTeam(team);
}

@Override
public void buildVideo(String video) {
game.setVideo(video);
}

@Override
public void buildCommunity(String community) {
game.setCommunity(community);
}

@Override
public Game makeGame() {
return game;
}
}

其实现在我们已经可以使用我们创建的builder进行创建游戏了,不过这里可以定义一个管理角色的对象,对builder进行管理,用它来进行管理对象的创建,这里我们定义一个游戏经理类GameManager。

GameManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GameManager {
private GameBuilder gameBuilder;

public void setGameBuilder(GameBuilder gameBuilder) {
this.gameBuilder = gameBuilder;
}

public Game makeGame(String name, String introduce, String team, String video, String community) {
gameBuilder.buildName(name);
gameBuilder.buildIntroduce(introduce);
gameBuilder.buildTeam(team);
gameBuilder.buildVideo(video);
gameBuilder.buildCommunity(community);
return gameBuilder.makeGame();
}

}

写到这里我们已经完成了一个标准的建造者模式代码,写一个测试类来测试一下。
Test.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {
public static void main(String[] args) {
GameBuilder gameBuilder = new ActionGameBuilder();

GameManager manager = new GameManager();
manager.setGameBuilder(gameBuilder);

Game game = manager.makeGame("游戏名称",
"这是一个很好玩的动作游戏",
"制作团队",
"游戏宣传视频",
"游戏社区");
System.out.println(game);
}
}

控制台可以看到创建的游戏信息。
1
2
3
Game{name='游戏名称', introduce='这是一个很好玩的动作游戏', team='制作团队', video='游戏宣传视频', community='游戏社区'}

Process finished with exit code 0

2.UML

   我们可以看到标准的建造者模式的UML类图。

我们测试时通过游戏经理和动作游戏建造者创建了含有较多属性的游戏类,并不关心创建的过程和顺序,有很好的封装性,使创建和使用分离,并且有很好的扩展性,建造类之间独立,在一定程度上解耦。

三、演进版

1.代码实现

   更多时候我们需要一种可以链式调用的形式进行建造我们的对象,实现其实也很简单,我们可以创建一个静态内部类作为对象的builder,具体代码如下:

Game.java
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
public class Game {
private String name;
private String introduce;
private String team;
private String video;
private String community;

public Game(GameBuilder gameBuilder) {
this.name = gameBuilder.name;
this.introduce = gameBuilder.introduce;
this.team = gameBuilder.team;
this.video = gameBuilder.video;
this.community = gameBuilder.community;
}

@Override
public String toString() {
return "Game{" +
"name='" + name + '\'' +
", introduce='" + introduce + '\'' +
", team='" + team + '\'' +
", video='" + video + '\'' +
", community='" + community + '\'' +
'}';
}

public static class GameBuilder {
private String name;
private String introduce;
private String team;
private String video;
private String community;

public GameBuilder buildName(String name) {
this.name = name;
return this;
}

public GameBuilder buildIntroduce(String introduce) {
this.introduce = introduce;
return this;
}

public GameBuilder buildTeam(String team) {
this.team = team;
return this;
}

public GameBuilder buildVideo(String video) {
this.video = video;
return this;
}

public GameBuilder buildCommunity(String community) {
this.community = community;
return this;
}

public Game build() {
return new Game(this);
}
}

}

   跟标准版本不同的是,在builder中的buildXxx方法返回的是builder本身,这样我们就可以使用链式调用的方式进行创建,最后调用build方法返回我们的实体类。写一个测试类测试一下。
Test.java
1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
Game game = new Game.GameBuilder().buildName("游戏名称")
.buildIntroduce("这是一个很好玩的游戏")
.buildTeam("制作团队")
.buildVideo("介绍视频")
.buildCommunity("游戏社区")
.build();
System.out.println(game);
}
}

执行程序,可以看到我们创建的game的信息。
1
2
3
Game{name='游戏名称', introduce='这是一个很好玩的游戏', team='制作团队', video='介绍视频', community='游戏社区'}

Process finished with exit code 0

2.UML

   演进版本的UML类图更加简单,可以很清楚地看到,对于应用方,只需要使用对象的builder进行链式调用,最后调用build方法返回所需的对象实例即可。

四、总结

   以上介绍了两个版本的建造者模式的实现,总体来说都是比较简单,容易理解的。在日常应用中,第二种是比较常用的,各种开源框架也都广泛使用,比如Guava中CacheBuilder,都是通过第二种链式调用的方式创建对象,使用方便。对于以后的扩展维护也比较方便。