建造者模式

概念

建造者模式(英:Builder Pattern)是一种创建型设计模式,又名:生成器模式。GOF 给建造者模式的定义为:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这句话说的比较抽象,其实解释一下就是:将建造复杂对象的过程和组成对象的部件解耦。

用途

假设现在我们是一家网游设计公司,现在我们要”抄袭”梦幻西游这款游戏,你是该公司的游戏角色设计人员。你怎么设计出该游戏中的各种角色呢? 在梦幻西游来中包括人、仙、魔等种族的角色,而每种不同的种族的角色中又包含龙太子、逍遥生等具体的角色。

作为一个出色的开发人员,我们设计的角色生成系统应该包含以下功能和特性:

为了保证游戏平衡,所有角色的基本属性应该一致

因为角色的创建过程可能很复杂,所以角色的生成细节不应该对外暴露

随时可以新增角色

对某个具体角色的修改应该不影响其他角色

其实,对于角色的设计,我们可以使用抽象工厂模式,将同一种族的角色看成是一个产品族。但是,这样做可能存在一个问题,那就是我们可能要在每个角色的创建过程中都要从头到尾的构建一遍该角色。比如一个角色包含头部、身体。其中头部又包括脸部、和其他部位。其中脸部又包含眉毛、嘴巴、鼻子等部位。整个角色的创建过程是极其复杂的。很容易遗漏其中的某个步骤。

那么,我们可以将这些具体部位的创建工作和对象的创建进行解耦。这就是建造者模式。

实现方式

建造者模式包含如下角色:

Builder:抽象建造者(Builder)

ConcreteBuilder:具体建造者(CommonBuilder、SuperBuilder)

Director:指挥者(Director)

Product:产品角色(Role)

这里采用设计角色的例子,为了便于理解,我们只创建两个角色,分别是普通角色和超级角色。他们都有设置头部、脸部、身体、气血值、魔法值、能量值等方法。值得注意的是设置脸部是依赖于设置头部的,要有先后顺序。

产品角色:Role

1
2
3
4
5
6
7
8
9
10
11
12
public class Role {

private String head; //头部
private String face; //脸部(脸部依赖于头部)
private String body; //身体
private Double hp; //生命值
private Double sp; //能量值
private Double mp; //魔法值

//setter and getter
// toString
}

抽象建造者:Builder

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

protected Role role = new Role();

public abstract void buildHead();

public abstract void buildFace();

public abstract void buildBody();

public abstract void buildHp();

public abstract void buildSp();

public abstract void buildMp();

public Role getResult() {
return role;
}
}

具体建造者:

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
public class CommonRoleBuilder extends Builder {

private Role role = new Role();

@Override
public void buildHead() {
role.setBody("common head");
}

@Override
public void buildFace() {
role.setFace("common face");
}

@Override
public void buildBody() {
role.setBody("common body");
}

@Override
public void buildHp() {
role.setHp(100d);
}

@Override
public void buildSp() {
role.setSp(100d);
}

@Override
public void buildMp() {
role.setMp(100d);
}

@Override
public Role getResult() {
return role;
}
}

public class SuperRoleBuilder extends Builder {

private Role role = new Role();

@Override
public void buildHead() {
role.setBody("suoer head");
}

@Override
public void buildFace() {
role.setFace("super face");
}

@Override
public void buildBody() {
role.setBody("super body");
}

@Override
public void buildHp() {
role.setHp(120d);
}

@Override
public void buildSp() {
role.setSp(120d);
}

@Override
public void buildMp() {
role.setMp(120d);
}

@Override
public Role getResult() {
return role;
}
}

指挥者:

1
2
3
4
5
6
7
8
9
10
11
public class Director {

public void construct(Builder builder){
builder.buildBody();
builder.buildHead();
builder.buildFace();
builder.buildHp();
builder.buildMp();
builder.buildSp();
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {

public static void main(String[] args) {

Director director = new Director();
Builder commonBuilder = new CommonRoleBuilder();

director.construct(commonBuilder);
Role commonRole = commonBuilder.getResult();
System.out.println(commonRole);

}
}


再回到之前的需求,看看我们是否都满足?

由于建造角色的过程比较复杂,其中还有相互依赖关系(如脸部依赖于头部),所以我们使用建造者模式将建造复杂对象的过程和组成对象的部件解耦。这样既保证了基本属性全都一致(这里的一致指的是该包含的应该全都包含)也封装了其中的具体实现细节。

同时,在修改某个具体角色的时候我们只需要修改对应的具体角色就可以了,不会影响到其他角色。

如果需要新增角色,只要再增加一个具体建造者,并在该建造者中写好具体细节的建造部分代码就OK了。

适用环境

在以下情况下可以使用建造者模式:

需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。

需要生成的产品对象的属性相互依赖,需要指定其生成顺序。

对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。

隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

参考资料:

  1. hollischuang
  2. stackoverflow