简介:
Builder 模式,中文翻译为建造者模式或者构建者模式,也有人叫它生成器模式。
创建者模式主要包含以下四个角色:
- 产品(Product):表示将要被构建的复杂对象。
- 抽象创建者(Abstract Builder):定义构建产品的接口,通常包含创建和获取产品的方法。
- 具体创建者(Concrete Builder):实现抽象创建者定义的接口,为产品的各个部分提供具体实现。
- 指挥者(Director):负责调用具体创建者来构建产品的各个部分,控制构建过程。
为什么需要建造者模式?
需求一、根据复杂的配置项进行定制化构建
首先,我们先看一个mybaits中经典的案例,这个案例中使用了装饰器和创建者设计模式:
public class CacheBuilder {
private final String id;
private Class<? extends Cache> implementation;
private final List<Class<? extends Cache>> decorators;
private Integer size;
private Long clearInterval;
private boolean readWrite;
private Properties properties;
private boolean blocking;
public CacheBuilder(String id) {
this.id = id;
this.decorators = new ArrayList<>();
}
public CacheBuilder size(Integer size) {
this.size = size;
return this;
}
public CacheBuilder clearInterval(Long clearInterval) {
this.clearInterval = clearInterval;
return this;
}
public CacheBuilder blocking(boolean blocking) {
this.blocking = blocking;
return this;
}
public CacheBuilder properties(Properties properties) {
this.properties = properties;
return this;
}
public Cache build() {
setDefaultImplementations();
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
// 根据配置的装饰器对原有缓存进行增强,如增加淘汰策略等
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
cache = setStandardDecorators(cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache(cache);
}
return cache;
}
}
我们总结这个案例中的几个特点:
1、参数有必填项id,有很多可选填的内容,通常必选项id通过构造器传入,可选项通过方法传递。
2、真正的构建过程需要调用build方法,构建时需要根据已配置的成员变量的内容选择合适的装饰器,对目标cache进行增强。
需求二、实现不可变对象
创建者设计模式(Builder Design Pattern)可以实现不可变对象,即一旦创建完成,对象的状态就不能改变。这有助于保证对象的线程安全和数据完整性。下面是一个使用创建者设计模式实现的不可变对象的Java示例:
public final class ImmutablePerson {
private final String name;
private final int age;
private final String address;
private ImmutablePerson(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.address = builder.address;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
public static class Builder {
private String name;
private int age;
private String address;
public Builder() {
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setAddress(String address) {
this.address = address;
return this;
}
public ImmutablePerson build() {
return new ImmutablePerson(this);
}
}
}
在这个例子中,ImmutablePerson
类具有三个属性:name
、age
和 address
。这些属性都是 final
的,一旦设置就不能更改。ImmutablePerson
的构造函数是私有的,外部无法直接创建该类的实例。要创建一个 ImmutablePerson
实例,需要使用内部的 Builder
类。通过连续调用 Builder
类的方法,我们可以为 ImmutablePerson
设置属性。最后,通过调用 build()
方法,我们创建一个具有指定属性的不可变 ImmutablePerson
实例。
实际上,使用建造者模式创建对象,还能避免对象存在无效状态。我再举个例子解释一下。比如我们定义了一个长方形类,如果不使用建造者模式,采用先创建后 set 的方式,那就会导致在第一个 set 之后,对象处于无效状态。具体代码如下所示:
Rectangle r = new Rectange(); // r is valid
r.setWidth(2); // r is valid
r.setHeight(3); // r is invalid
为了避免这种无效状态的存在,我们就需要使用构造函数一次性初始化好所有的成员变量。如果构造函数参数过多,我们就需要考虑使用建造者模式,先设置建造者的变量,然后再一次性地创建对象,让对象一直处于有效状态。
实际上,如果我们并不是很关心对象是否有短暂的无效状态,也不是太在意对象是否是可变的。比如,对象只是用来映射数据库读出来的数据,那我们直接暴露 set() 方法来设置类的成员变量值是完全没问题的。而且,使用建造者模式来构建对象,代码实际上是有点重复的。