Java上的builder机制

1.简述

Java的Builder机制,主要是将复杂对象的构建构建过程与其具体表示分离, 使得相同的构建流程可以创建不同的产品实例。Builder模式是一种创建型 设计模式,针对复杂对象构建,复杂对象主要表现:

1.1 多参数构造器

  • 问题1:参数顺序的混乱 比如:HttpParam(url, method, params, headers, timeout, cache)等等,顺序容易搞混。
  • 问题2:重载爆炸 为了支持可选参数,需要写多个重载构造器,比如,支持仅传递url, 仅传递url+method的... Builder方案,通过链式调用,明确参数含义,无需关心顺序,可以选参数进行设置
    HttpParam param = new HttpParam.Builder()
      .url("https://api.example.com")
      .method("POST")
      .timeout(1000)
      .cache(true)
      .headers(headers)
      .build(); // 最终构建对象
    
    ### 1.2 保证对象的不可变性,线程安全 理想设计的目标,应该将HttpParam 设计为 不可变对象,所有字段final, 无setter方法,构建后无法修改,天然线程安全。 Builder 方案:所有参数在 build() 方法中一次性赋值给不可变字段,构建完成后对象状态完全确定,不存在「半成品对象」。

2. 实例展示

实际开发中,已网络请求参数为例进行讲解,

import java.util.HashMap;
import java.util.Map;

/**
 * 网络请求参数实体类(不可变设计)
 */
public class HttpRequestParam {
    // 核心字段:必选参数(url/method)+ 可选参数(headers/params/timeout)
    private final String url;          // 必选:请求地址
    private final String method;       // 必选:请求方法(GET/POST)
    private final Map<String, String> headers; // 可选:请求头
    private final Map<String, String> params;  // 可选:请求参数(Query/Form)
    private final int timeout;         // 可选:超时时间(默认3000ms)

    // 关键:私有构造器,仅允许 Builder 内部调用(避免外部直接创建不完整对象)
    private HttpRequestParam(Builder builder) {
        // 从 Builder 中获取参数,赋值给当前不可变字段
        this.url = builder.url;
        this.method = builder.method;
        this.headers = builder.headers;
        this.params = builder.params;
        this.timeout = builder.timeout;
    }

    // 仅提供 getter 方法,不提供 setter(保证不可变性)
    public String getUrl() { return url; }
    public String getMethod() { return method; }
    public Map<String, String> getHeaders() { return headers; }
    public Map<String, String> getParams() { return params; }
    public int getTimeout() { return timeout; }

    // 重写 toString(),方便打印调试
    @Override
    public String toString() {
        return "HttpRequestParam{" +
                "url='" + url + '\'' +
                ", method='" + method + '\'' +
                ", headers=" + headers +
                ", params=" + params +
                ", timeout=" + timeout +
                '}';
    }

    // 第二步:定义 Builder 内部类(核心构建逻辑)
    public static class Builder {
        // 1. 必选参数:无默认值,必须由外部设置
        private final String url;
        private final String method;

        // 2. 可选参数:有默认值,外部可按需修改
        private Map<String, String> headers = new HashMap<>(); // 默认空请求头
        private Map<String, String> params = new HashMap<>();  // 默认空请求参数
        private int timeout = 3000; // 默认超时时间 3000ms

        /**
         * Builder 构造器:强制传入必选参数
         * (外部创建 Builder 时,必须先设置 url 和 method,避免遗漏核心参数)
         */
        public Builder(String url, String method) {
            // 必选参数校验:提前拦截非法值(避免构建出无效对象)
            if (url == null || url.trim().isEmpty()) {
                throw new IllegalArgumentException("请求地址 url 不能为空!");
            }
            if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method)) {
                throw new IllegalArgumentException("仅支持 GET/POST 请求方法!");
            }
            this.url = url.trim(); // 去空格,保证参数整洁
            this.method = method.toUpperCase(); // 统一转为大写(避免大小写混乱)
        }

        /**
         * 可选参数:设置请求头(链式调用)
         * @param key 头字段名
         * @param value 头字段值
         * @return 返回 Builder 自身,支持链式调用
         */
        public Builder addHeader(String key, String value) {
            if (key != null && value != null) { // 过滤空值
                this.headers.put(key, value);
            }
            return this; // 关键:返回 this,实现链式调用
        }

        /**
         * 可选参数:批量设置请求头
         */
        public Builder addHeaders(Map<String, String> headers) {
            if (headers != null && !headers.isEmpty()) {
                this.headers.putAll(headers);
            }
            return this;
        }

        /**
         * 可选参数:设置请求参数(链式调用)
         */
        public Builder addParam(String key, String value) {
            if (key != null && value != null) {
                this.params.put(key, value);
            }
            return this;
        }

        /**
         * 可选参数:批量设置请求参数
         */
        public Builder addParams(Map<String, String> params) {
            if (params != null && !params.isEmpty()) {
                this.params.putAll(params);
            }
            return this;
        }

        /**
         * 可选参数:设置超时时间
         */
        public Builder setTimeout(int timeout) {
            if (timeout > 0) { // 校验:超时时间必须为正数
                this.timeout = timeout;
            }
            return this;
        }

        /**
         * 最终构建方法:创建 HttpRequestParam 对象
         * @return 不可变的 HttpRequestParam 实例
         */
        public HttpRequestParam build() {
            // 此处可做最终参数校验(如 headers/params 特殊规则)
            return new HttpRequestParam(this); // 调用外部类私有构造器
        }
    }
}

实际使用过程中,调用:

// 链式调用设置可选参数(addParam/addHeader/setTimeout)
HttpRequestParam fullParam = new HttpRequestParam.Builder(
        "https://api.example.com/login", // url
        "POST"                          // method
)
.addParam("username", "zhangsan") // 添加单个请求参数
.addParam("password", "123456")
.addHeader("Content-Type", "application/x-www-form-urlencoded") // 添加请求头
.addHeader("User-Agent", "Android-App/1.0")
.setTimeout(5000) // 覆盖默认超时,设为 5000ms
.build(); // 最终构建

Log.d("BuilderDemo", "完整请求参数:" + fullParam);
// 输出:HttpRequestParam{url='https://api.example.com/login', method='POST', 
// headers={Content-Type=application/x-www-form-urlencoded, User-Agent=Android-App/1.0}, 
// params={username=zhangsan, password=123456}, timeout=5000}

3.核心总结

  • 不可变目标类: HttpRequestParam 字段使用final, 无seter, 保证线程安全,避免对象被篡改
  • 私有构造器:private HttpRequestParam(Builder builder), 仅允许Builer创建对象,避免外部创建
  • Builder内部类: public static class Builder, 封装所有构建逻辑
  • 必选参数强制传入+可选参数: Builder 构造器要求url method,避免核心参数遗漏,并可以进行校验
  • 链式调用:每个设置方法返回this