设计模式之建造者模式

静态工厂和构造器有一个共同的局限性:不能很好的扩展到大量的可选参数。对于初始化参数很多的类,常规的做法是使用重载构造器,但是当参数很多的时候,客户端代码会很难写,并且较难阅读。

这时,还有另外一种替代方案,使用javaBean模式,在这种模式下先默认构造器创建对象,然后用setter方法设置需要的参数。遗憾的是,JavaBean模式自身有着很严重的缺点,因为构造过程分成了好几个调用,在构造过程中JavaBean可能处于不一致的状态,类无法仅仅通过检查构造器参数的有效性来保证一致性。

最终还有第三种替代方案,既能确保安全性,也能保证可读性,那就是建造者模式。

1. 建造者模式

直接看一个简单例子:

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
public class Person {
private final String name;
private final String address;
private final int age;
private final int sex;
private final String tel;

public static class Builder {
//Required parameters
private final String name;
private final int age;
private final int sex;
//Optional parameters
private String address = "浙江杭州";
private String tel = "0571";

public Builder(String name, int age, int sex) {
this.name = name;
this.age = age;
this.sex = sex;
}

public Builder address(String address) {
this.address = address;
return this;
}

public Builder tel(String tel) {
this.tel = tel;
return this;
}

public Person build(){
return new Person(this);
}
}
private Person(Builder builder){
name = builder.name;
address = builder.address;
sex = builder.sex;
age = builder.age;
tel = builder.tel;
}

public static void main(String[] args){
Person p = new Builder("zhaozhengkang",25,1).address("yuhang").tel("123456789").build();
}
}

builder的设值方法返回builder本身,以便把调用连接起来形成一个流式的API.

2. 类层次中使用建造者模式 (effective java rule2,30)

使用平行层次结构的builder时,各自嵌套在相应类中。抽象类有抽象类的builder,具体类有具体类的builder。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public abstract class Pizza {
public enum Topping {
HAM, MUSHROOM, ONION, PEPPER, SAUSAGE
}
final Set<Topping> toppings;

abstract static class Builder<T extends Builder<T>>{
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping){
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
protected abstract T self();
}
Pizza(Builder<?> builder){
toppings = builder.toppings.clone();
}
}

Builder<T extends 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
26
public class NyPizza extends Pizza {
public enum Size{SMALL, MEDIUM, LARGER}
private final Size size;
public static class NyPizzaBuilder extends Pizza.Builder<NyPizzaBuilder>{
private final Size size;
public NyPizzaBuilder(Size size){
this.size = Objects.requireNonNull(size);
}
@Override
public NyPizza build() {
return new NyPizza(this);
}
@Override
protected NyPizzaBuilder self() {
return this;
}
}
private NyPizza(NyPizzaBuilder builder) {
super(builder);
size = builder.size;
}

public static void main(String[] args){
NyPizza p = new NyPizzaBuilder(Size.SMALL).addTopping(Topping.SAUSAGE).addTopping(Topping.ONION).build();
}
}

继承时,必须将正在定义的类NyPizzaBuilder作为类型参数传给基类Pizza.Builder,否则无法编译。
自限定类型属于泛型知识,将另开一篇进行研究。


参考资料

《Effcitive Java》