Skip to content

建造者模式 (Builder Pattern)

一、定义

一句话概括:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

官方定义(GoF):Separate the construction of a complex object from its representation so that the same construction process can create different representations.

建造者模式的核心思想是:分步骤构建复杂对象。当对象有多个可选参数、需要复杂的初始化过程时,建造者模式可以将对象的"组装过程"与"最终表示"解耦。


二、解决的问题

2.1 什么场景下需要建造者模式?

  • 构造参数过多:一个类有十几个甚至几十个参数,构造器参数列表过长
  • 参数可选:很多参数是可选的,用 telescoping constructor(重叠构造器)方式会导致构造器爆炸
  • 对象构造复杂:创建对象需要多个步骤,且步骤之间有依赖关系
  • 不可变对象:需要创建不可变对象,且参数较多
  • 需要不同的表示:同样的构建过程可以产生不同的产品

2.2 不用建造者会有什么问题?

java
// 问题1:重叠构造器(Telescoping Constructor)—— 参数爆炸
public class Computer {
    private String cpu;
    private String ram;
    private String storage;
    private String gpu;
    private String motherboard;
    private String powerSupply;
    private String caseType;
    private boolean hasWifi;
    private boolean hasBluetooth;
    private boolean hasRGB;

    // 如果每个参数都可选,需要多少个构造器?2^10 = 1024 个!
    public Computer(String cpu, String ram) { ... }
    public Computer(String cpu, String ram, String storage) { ... }
    public Computer(String cpu, String ram, String storage, String gpu) { ... }
    // 噩梦...

    // 问题2:setter 方式 —— 无法保证不可变性
    //       对象创建后可以被修改,线程不安全
    Computer c = new Computer();
    c.setCpu("i7");      // 可能在 set 过程中被其他线程读取到不完整状态
    c.setRam("16GB");
    // ...
}

三、结构

3.1 文字描述

建造者模式包含四个角色:

  • Product(产品):要创建的复杂对象
  • Builder(抽象建造者):定义构建步骤的接口
  • ConcreteBuilder(具体建造者):实现构建步骤,提供获取产品的方法
  • Director(指挥者):控制构建过程,按照特定顺序调用建造者的方法
  • Client(客户端):使用指挥者或直接使用建造者创建产品

3.2 ASCII 类图

┌──────────────────┐        ┌──────────────────────┐
│     Director      │        │   <<interface>>       │
├──────────────────┤        │      Builder           │
│ - builder: Builder│───────>├──────────────────────┤
│ + construct()     │        │ + buildPartA(): void  │
│   : Product       │        │ + buildPartB(): void  │
└──────────────────┘        │ + buildPartC(): void  │
                            │ + getResult(): Product │
                            └──────────────────────┘


                            ┌──────────┴──────────┐
                            │                     │
                    ┌──────────────┐    ┌──────────────┐
                    │ConcreteBuilderA│    │ConcreteBuilderB│
                    ├──────────────┤    ├──────────────┤
                    │+buildPartA() │    │+buildPartA() │
                    │+buildPartB() │    │+buildPartB() │
                    │+getResult()  │    │+getResult()  │
                    └──────────────┘    └──────────────┘
                            │                     │
                            ▼                     ▼
                    ┌──────────────┐    ┌──────────────┐
                    │   Product    │    │   Product    │
                    │  (不同表示)   │    │  (不同表示)   │
                    └──────────────┘    └──────────────┘

3.3 时序图

Client         Director         Builder        Product
  │                │                │              │
  │─construct()───>│                │              │
  │                │─buildPartA()──>│              │
  │                │                │──new PartA──>│
  │                │─buildPartB()──>│              │
  │                │                │──new PartB──>│
  │                │─buildPartC()──>│              │
  │                │                │──new PartC──>│
  │                │─getResult()───>│              │
  │                │<──Product──────│              │
  │<──Product──────│                │              │

四、代码实现

4.1 基础实现

4.1.1 传统建造者模式

java
/**
 * 传统建造者模式 —— 电脑组装
 * 包含 Director 角色,控制构建步骤
 */
public class TraditionalBuilderDemo {

    // ========== 1. 产品:电脑 ==========
    public static class Computer {
        private String cpu;
        private String ram;
        private String storage;
        private String gpu;
        private String os;

        // 构造器为 package-private,只有 Builder 能创建
        Computer() {}

        public void setCpu(String cpu) { this.cpu = cpu; }
        public void setRam(String ram) { this.ram = ram; }
        public void setStorage(String storage) { this.storage = storage; }
        public void setGpu(String gpu) { this.gpu = gpu; }
        public void setOs(String os) { this.os = os; }

        @Override
        public String toString() {
            return "Computer{cpu='" + cpu + "', ram='" + ram + "', storage='" + storage +
                   "', gpu='" + gpu + "', os='" + os + "'}";
        }
    }

    // ========== 2. 抽象建造者 ==========
    public interface ComputerBuilder {
        void buildCpu();
        void buildRam();
        void buildStorage();
        void buildGpu();
        void installOs();
        Computer getResult();
    }

    // ========== 3. 具体建造者:游戏电脑 ==========
    public static class GamingComputerBuilder implements ComputerBuilder {
        private final Computer computer = new Computer();

        @Override
        public void buildCpu() {
            computer.setCpu("Intel Core i9-14900K");
            System.out.println("  [游戏电脑] 安装顶级 CPU: i9-14900K");
        }

        @Override
        public void buildRam() {
            computer.setRam("64GB DDR5 6000MHz");
            System.out.println("  [游戏电脑] 安装大内存: 64GB DDR5");
        }

        @Override
        public void buildStorage() {
            computer.setStorage("2TB NVMe SSD");
            System.out.println("  [游戏电脑] 安装高速固态: 2TB NVMe");
        }

        @Override
        public void buildGpu() {
            computer.setGpu("NVIDIA RTX 4090");
            System.out.println("  [游戏电脑] 安装顶级显卡: RTX 4090");
        }

        @Override
        public void installOs() {
            computer.setOs("Windows 11 Pro");
            System.out.println("  [游戏电脑] 安装系统: Windows 11 Pro");
        }

        @Override
        public Computer getResult() {
            return computer;
        }
    }

    // ========== 4. 具体建造者:办公电脑 ==========
    public static class OfficeComputerBuilder implements ComputerBuilder {
        private final Computer computer = new Computer();

        @Override
        public void buildCpu() {
            computer.setCpu("Intel Core i5-13400");
            System.out.println("  [办公电脑] 安装主流 CPU: i5-13400");
        }

        @Override
        public void buildRam() {
            computer.setRam("16GB DDR4 3200MHz");
            System.out.println("  [办公电脑] 安装标准内存: 16GB DDR4");
        }

        @Override
        public void buildStorage() {
            computer.setStorage("512GB SSD");
            System.out.println("  [办公电脑] 安装固态硬盘: 512GB SSD");
        }

        @Override
        public void buildGpu() {
            computer.setGpu("集成显卡");
            System.out.println("  [办公电脑] 使用集成显卡");
        }

        @Override
        public void installOs() {
            computer.setOs("Windows 11 Home");
            System.out.println("  [办公电脑] 安装系统: Windows 11 Home");
        }

        @Override
        public Computer getResult() {
            return computer;
        }
    }

    // ========== 5. 指挥者:控制构建步骤 ==========
    public static class ComputerDirector {

        public Computer buildGamingPC(ComputerBuilder builder) {
            System.out.println("\n===== 开始组装游戏电脑 =====");
            builder.buildCpu();
            builder.buildRam();
            builder.buildGpu();
            builder.buildStorage();
            builder.installOs();
            System.out.println("===== 游戏电脑组装完成 =====\n");
            return builder.getResult();
        }

        public Computer buildOfficePC(ComputerBuilder builder) {
            System.out.println("\n===== 开始组装办公电脑 =====");
            builder.buildCpu();
            builder.buildRam();
            builder.buildStorage();  // 办公电脑先装存储再装显卡
            builder.buildGpu();
            builder.installOs();
            System.out.println("===== 办公电脑组装完成 =====\n");
            return builder.getResult();
        }
    }

    // ========== 6. 测试 ==========
    public static void main(String[] args) {
        ComputerDirector director = new ComputerDirector();

        // 构建游戏电脑
        ComputerBuilder gamingBuilder = new GamingComputerBuilder();
        Computer gamingPC = director.buildGamingPC(gamingBuilder);
        System.out.println("最终产品: " + gamingPC);

        // 构建办公电脑
        ComputerBuilder officeBuilder = new OfficeComputerBuilder();
        Computer officePC = director.buildOfficePC(officeBuilder);
        System.out.println("最终产品: " + officePC);
    }
}

4.2 进阶实现

4.2.1 链式调用建造者(Lombok @Builder 原理)

java
/**
 * 链式调用建造者 —— 现代 Java 中最常用的建造者变体
 * 省略了 Director 角色,Builder 本身控制构建流程
 *
 * 这正是 Lombok @Builder 注解的底层原理
 */
public class FluentBuilderDemo {

    // ========== 产品:不可变对象 ==========
    public static class HttpRequest {
        // 所有字段都是 final,保证不可变性
        private final String url;
        private final String method;
        private final Map<String, String> headers;
        private final String body;
        private final int connectTimeout;
        private final int readTimeout;
        private final boolean followRedirects;
        private final int maxRetries;

        // 私有构造器,只能通过 Builder 创建
        private HttpRequest(Builder builder) {
            this.url = builder.url;
            this.method = builder.method;
            this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers));
            this.body = builder.body;
            this.connectTimeout = builder.connectTimeout;
            this.readTimeout = builder.readTimeout;
            this.followRedirects = builder.followRedirects;
            this.maxRetries = builder.maxRetries;
        }

        @Override
        public String toString() {
            return "HttpRequest{url='" + url + "', method='" + method + "', headers=" + headers +
                   ", body='" + body + "', connectTimeout=" + connectTimeout +
                   ", readTimeout=" + readTimeout + ", followRedirects=" + followRedirects +
                   ", maxRetries=" + maxRetries + "}";
        }

        // ========== 静态内部 Builder 类 ==========
        public static class Builder {
            // 必填参数
            private final String url;
            private final String method;

            // 可选参数(带默认值)
            private Map<String, String> headers = new HashMap<>();
            private String body = "";
            private int connectTimeout = 5000;
            private int readTimeout = 10000;
            private boolean followRedirects = true;
            private int maxRetries = 0;

            // 构造器接收必填参数
            public Builder(String url, String method) {
                this.url = url;
                this.method = method;
            }

            // 每个 setter 返回 this,实现链式调用
            public Builder addHeader(String key, String value) {
                this.headers.put(key, value);
                return this;
            }

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

            public Builder connectTimeout(int timeoutMs) {
                this.connectTimeout = timeoutMs;
                return this;
            }

            public Builder readTimeout(int timeoutMs) {
                this.readTimeout = timeoutMs;
                return this;
            }

            public Builder followRedirects(boolean follow) {
                this.followRedirects = follow;
                return this;
            }

            public Builder maxRetries(int maxRetries) {
                this.maxRetries = maxRetries;
                return this;
            }

            // build() 方法完成构建,可以在此处做参数校验
            public HttpRequest build() {
                // 构建前校验
                if (url == null || url.isEmpty()) {
                    throw new IllegalArgumentException("URL 不能为空");
                }
                if (connectTimeout <= 0 || readTimeout <= 0) {
                    throw new IllegalArgumentException("超时时间必须大于 0");
                }
                return new HttpRequest(this);
            }
        }
    }

    // ========== 测试 ==========
    public static void main(String[] args) {
        // 链式调用,优雅构建复杂对象
        HttpRequest request = new HttpRequest.Builder("https://api.example.com/users", "POST")
                .addHeader("Content-Type", "application/json")
                .addHeader("Authorization", "Bearer token123")
                .body("{\"name\": \"张三\", \"age\": 25}")
                .connectTimeout(3000)
                .readTimeout(5000)
                .maxRetries(3)
                .followRedirects(false)
                .build();

        System.out.println(request);

        // 最简构建(只传必填参数)
        HttpRequest simpleRequest = new HttpRequest.Builder("https://api.example.com/health", "GET")
                .build();
        System.out.println(simpleRequest);
    }
}

4.2.2 Lombok @Builder 等价实现

java
/**
 * 模拟 Lombok @Builder 的完整实现
 *
 * Lombok @Builder 注解在编译时会生成类似以下的代码:
 *
 * @Builder
 * public class User {
 *     private String name;
 *     private int age;
 *     private String email;
 * }
 */
public class LombokStyleBuilder {

    public static class User {
        private final String name;
        private final int age;
        private final String email;
        private final String phone;
        private final String address;

        // Lombok 生成的构造器
        User(String name, int age, String email, String phone, String address) {
            this.name = name;
            this.age = age;
            this.email = email;
            this.phone = phone;
            this.address = address;
        }

        // Lombok 生成的静态 builder() 方法
        public static UserBuilder builder() {
            return new UserBuilder();
        }

        // Lombok 生成的 Builder 类
        public static class UserBuilder {
            private String name;
            private int age;
            private String email;
            private String phone;
            private String address;

            UserBuilder() {}

            public UserBuilder name(String name) {
                this.name = name;
                return this;
            }

            public UserBuilder age(int age) {
                this.age = age;
                return this;
            }

            public UserBuilder email(String email) {
                this.email = email;
                return this;
            }

            public UserBuilder phone(String phone) {
                this.phone = phone;
                return this;
            }

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

            public User build() {
                return new User(name, age, email, phone, address);
            }
        }

        @Override
        public String toString() {
            return "User{name='" + name + "', age=" + age + ", email='" + email +
                   "', phone='" + phone + "', address='" + address + "'}";
        }
    }

    public static void main(String[] args) {
        User user = User.builder()
                .name("张三")
                .age(30)
                .email("zhangsan@example.com")
                .phone("13800138000")
                .address("北京市朝阳区")
                .build();

        System.out.println(user);
    }
}

4.3 生产级实现(Spring Boot 场景)

java
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;

/**
 * 生产级 SQL 查询建造器 —— 类似于 MyBatis-Plus 的 QueryWrapper
 *
 * 业务场景:动态构建 SQL 查询,根据条件选择性添加 WHERE 子句
 */
public class SqlQueryBuilder {

    // ========== 产品:SQL 查询 ==========
    public static class SqlQuery {
        private final String select;
        private final String from;
        private final List<String> whereClauses;
        private final List<String> orderByColumns;
        private final String groupBy;
        private final Integer limit;
        private final Integer offset;
        private final List<Object> parameters;

        private SqlQuery(Builder builder) {
            this.select = builder.select;
            this.from = builder.from;
            this.whereClauses = new ArrayList<>(builder.whereClauses);
            this.orderByColumns = new ArrayList<>(builder.orderByColumns);
            this.groupBy = builder.groupBy;
            this.limit = builder.limit;
            this.offset = builder.offset;
            this.parameters = new ArrayList<>(builder.parameters);
        }

        public String toSql() {
            StringBuilder sql = new StringBuilder();
            sql.append("SELECT ").append(select != null ? select : "*");
            sql.append(" FROM ").append(from);

            if (!whereClauses.isEmpty()) {
                sql.append(" WHERE ").append(String.join(" AND ", whereClauses));
            }

            if (groupBy != null) {
                sql.append(" GROUP BY ").append(groupBy);
            }

            if (!orderByColumns.isEmpty()) {
                sql.append(" ORDER BY ").append(String.join(", ", orderByColumns));
            }

            if (limit != null) {
                sql.append(" LIMIT ").append(limit);
            }

            if (offset != null) {
                sql.append(" OFFSET ").append(offset);
            }

            return sql.toString();
        }

        public List<Object> getParameters() {
            return Collections.unmodifiableList(parameters);
        }
    }

    // ========== Builder ==========
    public static class Builder {
        private String select;
        private final String from;
        private final List<String> whereClauses = new ArrayList<>();
        private final List<String> orderByColumns = new ArrayList<>();
        private String groupBy;
        private Integer limit;
        private Integer offset;
        private final List<Object> parameters = new ArrayList<>();

        public Builder(String from) {
            this.from = from;
        }

        public Builder select(String... columns) {
            this.select = String.join(", ", columns);
            return this;
        }

        public Builder where(String clause) {
            this.whereClauses.add(clause);
            return this;
        }

        public Builder where(String clause, Object param) {
            this.whereClauses.add(clause);
            this.parameters.add(param);
            return this;
        }

        public Builder andEq(String column, Object value) {
            if (value != null) {
                this.whereClauses.add(column + " = ?");
                this.parameters.add(value);
            }
            return this;
        }

        public Builder andLike(String column, String value) {
            if (value != null && !value.isEmpty()) {
                this.whereClauses.add(column + " LIKE ?");
                this.parameters.add("%" + value + "%");
            }
            return this;
        }

        public Builder andBetween(String column, Object start, Object end) {
            if (start != null && end != null) {
                this.whereClauses.add(column + " BETWEEN ? AND ?");
                this.parameters.add(start);
                this.parameters.add(end);
            }
            return this;
        }

        public Builder andIn(String column, List<?> values) {
            if (values != null && !values.isEmpty()) {
                String placeholders = String.join(", ", Collections.nCopies(values.size(), "?"));
                this.whereClauses.add(column + " IN (" + placeholders + ")");
                this.parameters.addAll(values);
            }
            return this;
        }

        public Builder orderByAsc(String column) {
            this.orderByColumns.add(column + " ASC");
            return this;
        }

        public Builder orderByDesc(String column) {
            this.orderByColumns.add(column + " DESC");
            return this;
        }

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

        public Builder limit(int limit) {
            this.limit = limit;
            return this;
        }

        public Builder offset(int offset) {
            this.offset = offset;
            return this;
        }

        public Builder page(int pageNum, int pageSize) {
            this.offset = (pageNum - 1) * pageSize;
            this.limit = pageSize;
            return this;
        }

        public SqlQuery build() {
            if (from == null || from.isEmpty()) {
                throw new IllegalArgumentException("FROM 子句不能为空");
            }
            return new SqlQuery(this);
        }
    }

    // ========== 测试 ==========
    public static void main(String[] args) {
        // 动态构建复杂查询
        SqlQuery query = new SqlQuery.Builder("users u")
                .select("u.id", "u.name", "u.email", "u.created_at")
                .andEq("u.status", "ACTIVE")
                .andLike("u.name", "张")
                .andBetween("u.age", 18, 35)
                .andIn("u.department", List.of("DEV", "QA", "OPS"))
                .orderByDesc("u.created_at")
                .page(1, 20)
                .build();

        System.out.println("SQL: " + query.toSql());
        System.out.println("参数: " + query.getParameters());
    }
}

五、优缺点

优点

优点说明
参数灵活客户端可以按需设置参数,不必为所有参数传值
代码可读性好链式调用语义清晰,如 .name("张三").age(30)
不可变对象可以在 build() 时创建不可变对象,保证线程安全
构建过程可控在 build() 中可以做参数校验、默认值设置
符合单一职责原则构建逻辑与业务逻辑分离

缺点

缺点说明
代码量增加需要编写 Builder 类,代码量比直接使用构造器多
增加类数量每个产品类通常需要一个 Builder 内部类
不适合简单对象如果只有 2-3 个参数,使用建造者反而过度设计

六、适用场景

  1. 构造参数过多(通常超过 4 个):如 HTTP 请求构建、数据库连接配置
  2. 参数可选且有默认值:如用户搜索条件、报表配置
  3. 需要创建不可变对象:如配置对象、请求对象、DTO
  4. 需要构建不同表示:如导出不同格式的报表(PDF、Excel、CSV)
  5. SQL 动态查询构建:如 MyBatis-Plus 的 QueryWrapper、JPA 的 Specification
  6. 复杂对象分步创建:如文档生成器、邮件构建器
  7. 需要参数校验:在 build() 方法中统一校验,避免创建非法对象

七、JDK / Spring 框架中的实际应用

7.1 JDK 中的建造者

1. StringBuilder / StringBuffer
   - 最经典的建造者模式实现
   - append() 方法返回 this,链式调用
   - toString() 相当于 build()

2. java.util.stream.Stream.Builder
   - Stream API 中的建造者,用于构建流

3. java.lang.ProcessBuilder
   - 用于构建操作系统进程

4. java.util.Calendar.Builder (JDK 8+)
   - Calendar 的建造者

5. javax.swing.GroupLayout
   - 布局管理器的建造者风格 API

7.2 Spring 框架中的建造者

1. RestTemplate / WebClient
   - WebClient.create().uri(...).header(...).retrieve() 链式调用

2. UriComponentsBuilder
   - 构建 URI,支持链式添加参数

3. MockMvcRequestBuilders
   - Spring Test 中的请求构建器

4. ResponseEntity Builder
   - ResponseEntity.ok().header(...).body(...)

5. Spring Security 的 HttpSecurity
   - http.authorizeRequests().anyRequest().authenticated()...

7.3 知名第三方库

1. Lombok @Builder
   - 编译期自动生成建造者代码

2. OkHttp Request.Builder
   - new Request.Builder().url(...).header(...).build()

3. Retrofit
   - 基于注解的 HTTP 客户端,内部使用建造者模式

4. MyBatis-Plus QueryWrapper
   - LambdaQueryWrapper<User>().eq(User::getName, "张三").like(User::getEmail, "gmail")

5. Apache HttpClient
   - RequestBuilder.get().setUri(...).build()

八、与其他模式的关系

相关模式关系说明
工厂方法模式工厂方法关注"创建什么",建造者关注"怎么创建"。工厂方法一步创建,建造者分步创建
抽象工厂模式抽象工厂创建产品族,建造者创建复杂产品。两者可以组合:抽象工厂返回建造者
组合模式建造者常用于构建组合模式中的树形结构
原型模式建造者可以配合原型模式:先用原型克隆,再用建造者修改
单例模式建造者创建的通常不是单例,但建造者本身可以是单例

九、面试常见问题

Q1:建造者模式和工厂模式有什么区别?

答案

对比维度工厂模式建造者模式
创建方式一步创建分步创建
关注点创建什么产品如何组装产品
参数复杂度参数较少参数多且复杂
产品类型返回不同类型(多态)返回同一类型的不同表示
典型场景数据库连接、日志记录器HTTP 请求、SQL 查询、配置对象

一句话:工厂模式是"批发"(一次创建完整对象),建造者模式是"零售"(一步步组装)。


Q2:Lombok @Builder 是如何实现的?

答案: Lombok 使用 APT(Annotation Processing Tool) 在编译期生成代码:

  1. 为被注解的类生成一个静态内部类 XxxBuilder
  2. 为每个字段生成对应的 setter 方法(返回 XxxBuilder
  3. 生成 build() 方法,调用全参构造器创建对象
  4. 生成静态 builder() 方法返回 XxxBuilder 实例

本质是编译期代码生成,不影响运行时性能。


Q3:Director(指挥者)在建造者模式中是否必须?

答案: 不是必须的。在现代 Java 实践中,Director 角色常被省略,Builder 自己控制构建流程。两种形式各有适用场景:

  • 有 Director:构建步骤固定且顺序重要时(如电脑组装、文档生成),Director 封装构建顺序
  • 无 Director:参数可选且顺序无关时(如 HTTP 请求、配置对象),链式调用更灵活

Q4:建造者模式和 setter 方式有什么区别?

答案

对比维度建造者模式Setter 方式
不可变性可以创建不可变对象(final 字段)对象可变,线程不安全
一致性build() 时一次性创建,保证原子性设置过程中可能处于不一致状态
参数校验在 build() 中统一校验分散在各 setter 中,难以保证
代码风格链式调用,语义清晰逐行 set,代码冗长

Q5:建造者模式在什么情况下不适用?

答案

  1. 参数很少(少于 3 个):使用构造器或静态工厂方法更简单
  2. 参数都是必填的:直接用构造器更清晰
  3. 性能极度敏感:建造者会创建额外的 Builder 对象(但通常可忽略)
  4. 对象结构简单:没有复杂初始化逻辑

总结

建造者模式是处理"参数爆炸"的最佳方案,在现代 Java 开发中极为常用。核心要点:

  1. 链式调用是现代建造者最流行的形式,Lombok @Builder 就是最佳实践
  2. 不可变对象是建造者模式的重要应用场景,保证线程安全
  3. 参数校验放在 build() 方法中,可以避免创建非法对象
  4. Director 角色非必须,简单场景用链式调用即可
  5. 实际开发中,SQL 查询构建器HTTP 请求构建器是最常见的生产级应用