跳到主要内容

建造者模式

模式概述

定义

建造者模式(Builder Pattern)是一种创建型设计模式,它将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示

建造者模式要解决的问题

建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象(包含多个成员变量的对象)。用户只需要制定复杂对象的类型就可以得到该对象,而无需知道其内部的具体的构造细节。

提示

想象你去一家餐厅点餐,服务员会问你:"需要什么口味?需要加辣吗?需要配饮料吗?"等一系列问题。这个过程就像建造者模式,通过一步步设置不同的选项,最终完成一份完整的订单。

为什么需要建造者模式?

假设我们需要创建一个资源池配置类:

public class ResourcePoolConfig {
private String name;
private int maxTotal;
private int maxIdle;
private int minIdle;

public ResourcePoolConfig(String name, Integer maxTotal, Integer maxIdle, Integer minIdle) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("name should not be empty.");
}
this.name = name;

if (maxTotal != null) {
if (maxTotal <= 0) {
throw new IllegalArgumentException("maxTotal should be positive.");
}
this.maxTotal = maxTotal;
}

if (maxIdle != null) {
if (maxIdle < 0) {
throw new IllegalArgumentException("maxIdle should not be negative.");
}
this.maxIdle = maxIdle;
}

if (minIdle != null) {
if (minIdle < 0) {
throw new IllegalArgumentException("minIdle should not be negative.");
}
this.minIdle = minIdle;
}
}
}

这种实现方式有以下问题:

  1. 构造函数参数过多,容易出错。
  2. 参数之间可能有依赖关系,需要校验,破坏了不可变对象的封闭性。
  3. 代码可读性差,难以维护。

建造者模式实现

基本实现

public class ResourcePoolConfig {
private String name;
private int maxTotal;
private int maxIdle;
private int minIdle;

private ResourcePoolConfig(Builder builder) {
this.name = builder.name;
this.maxTotal = builder.maxTotal;
this.maxIdle = builder.maxIdle;
this.minIdle = builder.minIdle;
}

//建造者类
public static class Builder {
private static final int DEFAULT_MAX_TOTAL = 8;
private static final int DEFAULT_MAX_IDLE = 8;
private static final int DEFAULT_MIN_IDLE = 0;

private String name;
private int maxTotal = DEFAULT_MAX_TOTAL;
private int maxIdle = DEFAULT_MAX_IDLE;
private int minIdle = DEFAULT_MIN_IDLE;

public Builder setName(String name) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("name should not be empty.");
}
this.name = name;
return this;
}

public Builder setMaxTotal(int maxTotal) {
if (maxTotal <= 0) {
throw new IllegalArgumentException("maxTotal should be positive.");
}
this.maxTotal = maxTotal;
return this;
}

public Builder setMaxIdle(int maxIdle) {
if (maxIdle < 0) {
throw new IllegalArgumentException("maxIdle should not be negative.");
}
this.maxIdle = maxIdle;
return this;
}

public Builder setMinIdle(int minIdle) {
if (minIdle < 0) {
throw new IllegalArgumentException("minIdle should not be negative.");
}
this.minIdle = minIdle;
return this;
}

public ResourcePoolConfig build() {
// 校验逻辑放到这里来做,包括必填项校验、依赖关系校验、约束条件校验等
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("name should not be empty.");
}
if (maxIdle > maxTotal) {
throw new IllegalArgumentException("maxIdle cannot be greater than maxTotal.");
}
if (minIdle > maxTotal || minIdle > maxIdle) {
throw new IllegalArgumentException("minIdle cannot be greater than maxTotal or maxIdle.");
}

return new ResourcePoolConfig(this);
}
}
}

使用示例

// 使用建造者模式
ResourcePoolConfig config = new ResourcePoolConfig.Builder()
.setName("dbconnectionpool")
.setMaxTotal(16)
.setMaxIdle(10)
.setMinIdle(2)
.build();

应用场景

1. 构造参数过多

当一个类的构造函数参数超过4个,使用构造函数就会导致代码可读性差,使用建造者模式更合适。

2. 参数之间有依赖关系

当参数之间有一定的约束条件时,使用构造函数很难做参数校验,建造者模式可以在build()方法中统一校验。

3. 需要创建不可变对象

建造者模式创建的对象是不可变的,所有的属性都在构建时设置好。

最佳实践

1. 链式调用

返回this可以实现链式调用,提高代码的可读性:

Builder setXxx(xxx) {
this.xxx = xxx;
return this;
}

2. 参数校验

  • 必填参数校验可以放在setter方法中
  • 参数之间的依赖关系校验放在build()方法中
  • 提供默认值,减少客户端代码的设置

3. 不可变性

  • 将构造函数设置为private
  • 不提供setter方法
  • 所有属性设置为final

建造者模式变体

1. 静态内部类方式

public class ResourcePoolConfig {
private final String name;
private final int maxTotal;
private final int maxIdle;
private final int minIdle;

private ResourcePoolConfig(Builder builder) { /*...*/ }

public static class Builder { /*...*/ }
}

2. 链式调用方式

public class ResourcePoolConfig {
public ResourcePoolConfig name(String name) {
this.name = name;
return this;
}
// 其他setter方法
}

与其他模式的区别

1. 工厂模式 vs 建造者模式

  • 工厂模式:关注一个产品整体的创建方法
  • 建造者模式:关注一个产品各个部分的创建过程

2. 抽象工厂 vs 建造者模式

  • 抽象工厂:创建多个产品族中的产品
  • 建造者模式:一步步创建一个复杂的产品