建造者模式是将对象的构建封装起来解耦,方便调用者创建复杂的对象,无需知道构建的细节。
常见的使用场景就是一些复杂的连接对象,有非常多的配置,用户不可能清楚每一个属性,所以一般会通过建造者模式来提供属性的默认值,用户只需设置关键的属性,方便调用者使用。
下面根据nacos源码里的一个例子说明建造者模式
@Data
public class RestResult<T> implements Serializable {
private static final long serialVersionUID = 6095433538316185017L;
private int code;
private String message;
private T data;
public RestResult() {
}
public RestResult(int code, String message, T data) {
this.code = code;
this.setMessage(message);
this.data = data;
}
public RestResult(int code, T data) {
this.code = code;
this.data = data;
}
public RestResult(int code, String message) {
this.code = code;
this.setMessage(message);
}
public boolean ok() {
return this.code == 0 || this.code == 200;
}
/**
* 这里有一些简化,没有继承父类或接口,建造者模式实际可以有不同的实现子类
*/
public static <T> ResResultBuilder<T> builder() {
return new ResResultBuilder<T>();
}
public static final class ResResultBuilder<T> {
private int code;
private String errMsg;
private T data;
private ResResultBuilder() {
}
public ResResultBuilder<T> withCode(int code) {
// 当然这里可以再添加一些复杂的逻辑,比如设置了某属性值,其他属性就只能为某些值
this.code = code;
return this;
}
public ResResultBuilder<T> withMsg(String errMsg) {
this.errMsg = errMsg;
return this;
}
public ResResultBuilder<T> withData(T data) {
this.data = data;
return this;
}
/**
* Build result.
*
* @return result
*/
public RestResult<T> build() {
RestResult<T> restResult = new RestResult<T>();
restResult.setCode(code);
restResult.setMessage(errMsg);
restResult.setData(data);
return restResult;
}
}
}
下面相当于Director,用于指挥控制对象的创建
public class RestResultUtils {
public static <T> RestResult<T> success() {
return RestResult.<T>builder().withCode(200).build();
}
public static <T> RestResult<T> success(T data) {
return RestResult.<T>builder().withCode(200).withData(data).build();
}
public static <T> RestResult<T> success(int code, T data) {
return RestResult.<T>builder().withCode(code).withData(data).build();
}
public static <T> RestResult<T> failed() {
return RestResult.<T>builder().withCode(500).build();
}
public static <T> RestResult<T> failed(int code, T data) {
return RestResult.<T>builder().withCode(code).withData(data).build();
}
}
应用举例
经常用来提供那些拥有复杂属性的对象,用户可以只设置自己关心的部分属性,如spring框架提供的很多连接对象。
下面拿构建RedisCacheManager的源码举例:
@Bean
RedisCacheManager redisCacheManager() {
// 我们使用时只需要设置关键的属性即可
return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(lettuceConnectionFactory)
.withInitialCacheConfigurations(cacheConfigurationMap)
.disableCreateOnMissingCache()
.build();
}
这里其实也是用到了建造者模式,
public class RedisCacheManager{
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
private final boolean allowInFlightCacheCreation;
private RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
boolean allowInFlightCacheCreation) {
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
this.initialCacheConfiguration = new LinkedHashMap<>();
this.allowInFlightCacheCreation = allowInFlightCacheCreation;
}
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
// 通过builder来设置RedisCacheManager属性
public static class RedisCacheManagerBuilder {
private RedisCacheWriter cacheWriter;
private RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
private boolean enableTransactions;
boolean allowInFlightCacheCreation = true;
private RedisCacheManagerBuilder() {}
private RedisCacheManagerBuilder(RedisCacheWriter cacheWriter) {
this.cacheWriter = cacheWriter;
}
public static RedisCacheManagerBuilder fromConnectionFactory(RedisConnectionFactory connectionFactory) {
return new RedisCacheManagerBuilder(new DefaultRedisCacheWriter(connectionFactory));
}
public static RedisCacheManagerBuilder fromCacheWriter(RedisCacheWriter cacheWriter) {
return new RedisCacheManagerBuilder(cacheWriter);
}
public RedisCacheManagerBuilder cacheDefaults(RedisCacheConfiguration defaultCacheConfiguration) {
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null!");
this.defaultCacheConfiguration = defaultCacheConfiguration;
return this;
}
public RedisCacheManagerBuilder transactionAware() {
this.enableTransactions = true;
return this;
}
public RedisCacheManagerBuilder initialCacheNames(Set<String> cacheNames) {
Assert.notNull(cacheNames, "CacheNames must not be null!");
cacheNames.forEach(it -> withCacheConfiguration(it, defaultCacheConfiguration));
return this;
}
public RedisCacheManagerBuilder withInitialCacheConfigurations(
Map<String, RedisCacheConfiguration> cacheConfigurations) {
Assert.notNull(cacheConfigurations, "CacheConfigurations must not be null!");
cacheConfigurations.forEach((cacheName, configuration) -> Assert.notNull(configuration,
String.format("RedisCacheConfiguration for cache %s must not be null!", cacheName)));
this.initialCaches.putAll(cacheConfigurations);
return this;
}
public RedisCacheManagerBuilder withCacheConfiguration(String cacheName, RedisCacheConfiguration cacheConfiguration) {
Assert.notNull(cacheName, "CacheName must not be null!");
Assert.notNull(cacheConfiguration, "CacheConfiguration must not be null!");
this.initialCaches.put(cacheName, cacheConfiguration);
return this;
}
public RedisCacheManagerBuilder disableCreateOnMissingCache() {
this.allowInFlightCacheCreation = false;
return this;
} }
public RedisCacheManager build() {
Assert.state(cacheWriter != null, "CacheWriter must not be null! '.");
RedisCacheManager cm = new RedisCacheManager(cacheWriter, defaultCacheConfiguration, initialCaches, allowInFlightCacheCreation);
cm.setTransactionAware(enableTransactions);
return cm;
}
}
}
优点
- 易于解耦
将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同属性的产品。 - 易于精确控制对象的创建
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰 - 易于拓展
增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的属性差异性很大,则不适合使用建造者模式。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大