Builder模式详解:从困惑到理解

目录

  • [1. 什么是建造者模式?](#1. 什么是建造者模式?)
  • [2. 为什么需要建造者模式?](#2. 为什么需要建造者模式?)
  • [3. 基础实现](#3. 基础实现)
  • [4. 进阶实现](#4. 进阶实现)
  • [5. 实际应用场景](#5. 实际应用场景)
  • [6. 与其他方式的对比](#6. 与其他方式的对比)
  • [7. 总结](#7. 总结)

1. 什么是建造者模式?

建造者模式是一种创建对象的方式。想象你要组装一台电脑:

  • 传统方式:一次性告诉店员所有配置(CPU、内存、硬盘...),容易说错
  • 建造者方式:一步步选择(先选CPU,再选内存,再选硬盘...),最后组装

1.1 核心思想

java 复制代码
// 建造者模式的基本流程
对象 = 建造器
    .设置属性A(值A)
    .设置属性B(值B)
    .设置属性C(值C)
    .构建();

1.2 关键特点

  1. 分步骤构建:一个属性一个属性地设置
  2. 链式调用:可以连续调用方法
  3. 最终构建 :最后调用build()方法创建对象
  4. 不可变对象:创建后对象不能修改

2. 为什么需要建造者模式?

2.1 解决的问题

问题1:构造函数参数太多
java 复制代码
// ❌ 不好的方式:参数太多,容易搞错顺序
public class Computer {
    public Computer(String cpu, String memory, String storage, 
                   String graphics, String motherboard, boolean wifi, 
                   boolean bluetooth, String powerSupply, String caseType) {
        // 参数太多,容易传错
    }
}

// 使用时容易出错
Computer computer = new Computer("Intel i7", "32GB", "1TB", 
                               "RTX4070", "ASUS", true, false, "650W", "ATX");
//                                          ↑ 这是什么意思?
问题2:可选参数难处理
java 复制代码
// ❌ 不好的方式:需要很多重载的构造函数
public class Computer {
    public Computer(String cpu, String memory) { ... }
    public Computer(String cpu, String memory, String storage) { ... }
    public Computer(String cpu, String memory, String storage, String graphics) { ... }
    // 需要写很多个构造函数...
}
问题3:代码可读性差
java 复制代码
// ❌ 看不出每个参数的含义
Computer computer = new Computer("Intel i7", "32GB", "1TB", "RTX4070", null, true);

2.2 建造者模式的解决方案

java 复制代码
// ✅ 建造者模式:清晰、灵活、易读
Computer computer = new Computer.Builder()
    .setCpu("Intel i7-12700K")       // 清楚知道在设置CPU
    .setMemory("32GB DDR4")          // 清楚知道在设置内存
    .setStorage("1TB NVMe SSD")      // 清楚知道在设置存储
    .setGraphics("RTX 4070")         // 清楚知道在设置显卡
    .setWifi(true)                   // 清楚知道在设置WiFi
    .build();                        // 最后构建对象

3. 基础实现

3.1 第一个建造者模式示例

让我们从一个简单的学生类开始。这个例子会完整展示建造者模式的所有核心要素,帮助你彻底理解这个模式的运作机制。

设计思路

我们要创建一个学生类,包含姓名、年龄、学校、专业、GPA等信息。如果用传统的构造函数,参数会很多且容易搞错顺序。使用建造者模式,我们可以让创建过程更清晰、更安全。

java 复制代码
/**
 * 学生类 - 使用建造者模式创建
 */
public class Student {
    // 🔥 关键点1:所有字段都是final的,创建后不能修改
    // 这样设计的好处:
    // 1. 对象一旦创建就不可变,更安全
    // 2. 多线程环境下不用担心数据被意外修改
    // 3. 符合函数式编程的不可变对象思想
    private final String name;
    private final int age;
    private final String school;
    private final String major;
    private final double gpa;
    
    // 🔥 关键点2:私有构造器,只能通过Builder创建
    // 为什么要设为私有?
    // 1. 防止用户直接 new Student(),强制使用Builder
    // 2. 确保所有Student对象都经过Builder的验证流程
    // 3. 维护设计模式的完整性和一致性
    private Student(StudentBuilder builder) {
        // 🔥 关键点3:从Builder对象中提取数据
        // 这里发生了数据的"转移":从可变的Builder转移到不可变的Student
        // builder参数包含了用户通过链式调用设置的所有信息
        this.name = builder.name;
        this.age = builder.age;
        this.school = builder.school;
        this.major = builder.major;
        this.gpa = builder.gpa;
    }
    
    // getter方法
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getSchool() { return school; }
    public String getMajor() { return major; }
    public double getGpa() { return gpa; }
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school='" + school + '\'' +
                ", major='" + major + '\'' +
                ", gpa=" + gpa +
                '}';
    }
    
    /**
     * 🔥 关键点4:建造者内部类
     * 为什么要设计成静态内部类?
     * 1. 静态内部类可以访问外部类的私有构造器
     * 2. 不需要外部类的实例就能创建Builder(节省内存)
     * 3. 逻辑上Builder属于Student的一部分,放在一起更合理
     * 4. 用户使用时直接 new Student.StudentBuilder() 很直观
     */
    public static class StudentBuilder {
        // 🔥 关键点5:Builder的字段用来临时存储配置信息
        // 注意:这些字段不是final的,因为需要通过setter方法修改
        // 这些字段就像"收集篮",用来收集用户的配置选择
        private String name;        // 临时存储姓名
        private int age;            // 临时存储年龄  
        private String school;      // 临时存储学校
        private String major;       // 临时存储专业
        private double gpa;         // 临时存储GPA
        
        // 🔥 关键点6:链式设置方法(Fluent Interface)
        // 这是建造者模式最重要的特性之一
        public StudentBuilder setName(String name) {
            // 第1步:将用户传入的参数保存到Builder的字段中
            this.name = name;
            
            // 第2步:返回当前Builder对象本身(this)
            // 这一步是实现链式调用的关键!
            // 返回this意味着用户可以继续在这个Builder上调用其他方法
            return this;  
        }
        
        // 🔥 所有setter方法都遵循相同的模式:
        // 1. 保存参数到对应字段
        // 2. 返回this支持链式调用
        public StudentBuilder setAge(int age) {
            this.age = age;
            return this;  // 继续支持链式调用
        }
        
        public StudentBuilder setSchool(String school) {
            this.school = school;
            return this;  // 继续支持链式调用
        }
        
        public StudentBuilder setMajor(String major) {
            this.major = major;
            return this;  // 继续支持链式调用
        }
        
        public StudentBuilder setGpa(double gpa) {
            this.gpa = gpa;
            return this;  // 继续支持链式调用
        }
        
        // 🔥 关键点7:最终构建方法 - 建造者模式的核心
        // 这个方法是整个建造者模式的"收官之作"
        public Student build() {
            // 🔥 第一阶段:数据验证
            // 为什么要在build()中验证?
            // 1. 集中验证:所有验证逻辑都在一个地方,便于维护
            // 2. 延迟验证:用户可能会多次修改属性,最后验证更高效
            // 3. 完整性检查:可以验证多个字段之间的关系
            if (name == null || name.trim().isEmpty()) {
                throw new IllegalArgumentException("学生姓名不能为空");
            }
            if (age < 0 || age > 150) {
                throw new IllegalArgumentException("年龄必须在0-150之间");
            }
            
            // 可以添加更复杂的业务逻辑验证
            // 比如:if (gpa > 4.0 && school.equals("某些学校")) { ... }
            
            // 🔥 第二阶段:创建最终对象
            // 调用Student的私有构造器,将当前Builder对象传递过去
            // 此时发生"数据转移":从可变的Builder变成不可变的Student
            return new Student(this);
            // 注意:这里的this指向当前Builder对象,包含了所有用户设置的数据
        }
    }
}

3.2 如何使用

现在让我们看看如何使用刚才创建的Student建造者。这个使用示例会展示建造者模式在实际应用中的灵活性和便利性。

java 复制代码
public class StudentExample {
    public static void main(String[] args) {
        // 🔥 示例1:完整配置的学生对象
        // 这里演示了建造者模式的完整流程
        Student student1 = new Student.StudentBuilder()  // ←第1步:创建Builder对象
                .setName("张三")                          // ←第2步:设置姓名,返回Builder
                .setAge(20)                              // ←第3步:设置年龄,返回Builder
                .setSchool("北京大学")                    // ←第4步:设置学校,返回Builder
                .setMajor("计算机科学")                   // ←第5步:设置专业,返回Builder
                .setGpa(3.8)                            // ←第6步:设置GPA,返回Builder
                .build();                               // ←第7步:调用build()创建Student对象
        
        System.out.println("完整配置的学生:" + student1);
        
        // 🔥 示例2:部分配置的学生对象
        // 建造者模式的一个重要优势:不需要设置所有属性
        // 可以根据实际需要选择设置哪些属性
        Student student2 = new Student.StudentBuilder()
                .setName("李四")                         // 只设置必需的基本信息
                .setAge(19)
                .setSchool("清华大学")
                // 注意:这里没有设置major和gpa,它们会是默认值(null或0)
                .build();
        
        System.out.println("部分配置的学生:" + student2);
        
        // 🔥 示例3:演示链式调用的本质
        // 让我们分解链式调用,看看每一步都发生了什么
        System.out.println("\n=== 分解链式调用过程 ===");
        
        // 第1步:创建Builder
        Student.StudentBuilder builder = new Student.StudentBuilder();
        System.out.println("第1步:创建了Builder对象");
        
        // 第2步:设置姓名
        builder = builder.setName("王五");
        System.out.println("第2步:设置姓名后,返回的还是同一个Builder对象");
        
        // 第3步:设置年龄
        builder = builder.setAge(21);
        System.out.println("第3步:设置年龄后,继续返回Builder对象");
        
        // 第4步:最终构建
        Student student3 = builder.build();
        System.out.println("第4步:调用build()创建最终的Student对象:" + student3);
        
        // 🔥 示例4:演示验证机制
        // 当我们提供不合法的数据时会发生什么?
        try {
            Student invalidStudent = new Student.StudentBuilder()
                    .setName("")                    // 空姓名(不合法)
                    .setAge(-5)                     // 负数年龄(不合法)
                    .build();
        } catch (IllegalArgumentException e) {
            System.out.println("\n验证失败:" + e.getMessage());
            System.out.println("这证明了build()方法中的验证逻辑生效了!");
        }
    }
}

3.3 关键点深入解释

🔥 链式调用的工作原理详解

问题 :为什么每个setter方法都要返回this

java 复制代码
public StudentBuilder setName(String name) {
    this.name = name;
    return this;  // 🔥 这一行是链式调用的核心!
}

答案:这是实现**流畅接口(Fluent Interface)**的关键技术。让我们深入理解:

java 复制代码
// 🔥 内存层面的执行过程分析
Student.StudentBuilder builder = new Student.StudentBuilder();
// 此时内存中有一个Builder对象,假设地址是 @12345

builder = builder.setName("张三");    
// setName方法执行:
// 1. this.name = "张三"  (设置Builder对象@12345的name字段)
// 2. return this;       (返回Builder对象@12345的引用)
// 结果:builder变量仍然指向同一个对象@12345

builder = builder.setAge(20);         
// setAge方法执行:
// 1. this.age = 20      (设置Builder对象@12345的age字段) 
// 2. return this;       (返回Builder对象@12345的引用)
// 结果:builder变量仍然指向同一个对象@12345

// 🔥 关键洞察:始终是同一个Builder对象在被修改!

为什么可以链式调用?

java 复制代码
// 原理分析:方法调用的返回值可以继续调用方法
new Student.StudentBuilder()          // 创建Builder对象,返回Builder引用
    .setName("张三")                  // 在Builder上调用setName,返回同一个Builder
    .setAge(20)                      // 在返回的Builder上调用setAge,返回同一个Builder
    .setSchool("北大")                // 在返回的Builder上调用setSchool,返回同一个Builder
    .build();                        // 在返回的Builder上调用build,返回Student对象

// 等价于:
Student.StudentBuilder temp1 = new Student.StudentBuilder();
Student.StudentBuilder temp2 = temp1.setName("张三");        // temp2 == temp1 (同一个对象)
Student.StudentBuilder temp3 = temp2.setAge(20);            // temp3 == temp1 (同一个对象)  
Student.StudentBuilder temp4 = temp3.setSchool("北大");      // temp4 == temp1 (同一个对象)
Student finalStudent = temp4.build();

如果不返回this会怎样?

java 复制代码
// ❌ 错误的设计:不返回this
public void setName(String name) {  // 注意:返回类型是void
    this.name = name;
    // 没有return this;
}

// 使用时的问题:
Student.StudentBuilder builder = new Student.StudentBuilder();
builder.setName("张三");        // 这个方法没有返回值
    .setAge(20);               // ❌ 编译错误!void类型不能继续调用方法
🔥 为什么构造器必须是私有的?

设计原理 :私有构造器是建造者模式的强制约束机制

java 复制代码
private Student(StudentBuilder builder) {
    // 🔥 私有构造器的三大作用:
    // 1. 访问控制:只有同一个类内部可以调用
    // 2. 强制使用Builder:用户无法绕过Builder直接创建对象
    // 3. 保证一致性:所有Student对象都经过相同的创建流程
}

对比分析

java 复制代码
// ❌ 如果构造器是public的会发生什么?
public Student(String name, int age, String school, String major, double gpa) {
    this.name = name;
    this.age = age;
    // ...
}

// 用户就可以绕过Builder:
Student student1 = new Student("张三", 20, "北大", "计算机", 3.8);  // 直接构造
Student student2 = new Student.StudentBuilder().setName("李四").build();  // Builder方式

// 这样就有了两种创建方式,破坏了设计的一致性!
// 而且直接构造的方式没有经过Builder的验证逻辑
java 复制代码
// ✅ 私有构造器确保唯一的创建路径
private Student(StudentBuilder builder) { /* ... */ }

// 用户只能这样创建:
Student student = new Student.StudentBuilder()  // 必须使用Builder
    .setName("张三")
    .setAge(20)
    .build();  // 经过验证

// new Student(...);  // ❌ 编译错误:构造器不可见
🔥 为什么字段必须是final的?

设计原理final关键字创建真正不可变的对象

java 复制代码
private final String name;  // 🔥 final的三重保障:

深入分析

java 复制代码
public class Student {
    private final String name;     // ✅ final字段
    private final int age;         // ✅ final字段
    
    private Student(StudentBuilder builder) {
        this.name = builder.name;   // ✅ 构造器中一次性赋值
        this.age = builder.age;     // ✅ 之后再也不能修改
    }
    
    // ✅ 只有getter,没有setter
    public String getName() { 
        return name; 
    }
    
    // ❌ 如果有setter就破坏了不可变性
    // public void setName(String name) { this.name = name; }  // 编译错误!final字段不能重新赋值
}

不可变对象的优势

java 复制代码
// 🔥 优势1:线程安全
// 多个线程同时访问Student对象,不会有数据竞争问题
Student student = new Student.StudentBuilder().setName("张三").build();

Thread thread1 = new Thread(() -> {
    String name = student.getName();  // ✅ 安全读取,不会被其他线程修改
});

Thread thread2 = new Thread(() -> {
    String name = student.getName();  // ✅ 安全读取,得到的总是一致的数据
});

// 🔥 优势2:可以安全地共享和缓存
Map<String, Student> studentCache = new HashMap<>();
studentCache.put("张三", student);  // ✅ 可以安全缓存,因为对象不会变化

// 🔥 优势3:作为Map的key是安全的
Map<Student, String> grades = new HashMap<>();
grades.put(student, "A+");  // ✅ Student对象不会变化,hashCode保持稳定

对比可变对象的问题

java 复制代码
// ❌ 如果Student是可变的:
public class MutableStudent {
    private String name;  // 不是final
    
    public void setName(String name) {  // 提供了setter
        this.name = name;
    }
}

// 使用时的问题:
MutableStudent student = new MutableStudent("张三");
Map<MutableStudent, String> grades = new HashMap<>();
grades.put(student, "A+");

student.setName("李四");  // ❌ 对象被修改了!
String grade = grades.get(student);  // ❌ 可能返回null,因为hashCode变了

4. 进阶实现

4.1 带默认值的建造者

在实际项目中,我们经常需要为配置对象提供合理的默认值。这样用户只需要修改他们关心的部分,其他配置可以使用默认值,大大提高了易用性。

设计思路

  • 默认值策略:为每个字段提供生产环境中最常用的配置
  • 覆盖机制:用户可以选择性地覆盖任何默认值
  • 智能默认值:根据使用场景提供最合理的初始配置
java 复制代码
/**
 * 电脑配置类 - 带默认值的建造者
 * 这个例子展示了如何在Builder中预设合理的默认值
 */
public class Computer {
    private final String cpu;
    private final String memory;
    private final String storage;
    private final String graphics;
    private final boolean wifi;
    private final boolean bluetooth;
    
    private Computer(ComputerBuilder builder) {
        // 🔥 数据转移:从Builder的临时存储转移到Computer的永久存储
        // 这里转移的可能是用户自定义的值,也可能是Builder预设的默认值
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.storage = builder.storage;
        this.graphics = builder.graphics;
        this.wifi = builder.wifi;
        this.bluetooth = builder.bluetooth;
    }
    
    // getter方法...
    public String getCpu() { return cpu; }
    public String getMemory() { return memory; }
    public String getStorage() { return storage; }
    public String getGraphics() { return graphics; }
    public boolean hasWifi() { return wifi; }
    public boolean hasBluetooth() { return bluetooth; }
    
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", memory='" + memory + '\'' +
                ", storage='" + storage + '\'' +
                ", graphics='" + graphics + '\'' +
                ", wifi=" + wifi +
                ", bluetooth=" + bluetooth +
                '}';
    }
    
    public static class ComputerBuilder {
        // 🔥 关键设计:在字段声明时直接初始化默认值
        // 这种方式的优势:
        // 1. 用户可以直接调用build()而不设置任何参数,获得一个可用的默认配置
        // 2. 默认值集中管理,便于维护和修改
        // 3. 新手友好:即使不了解所有配置项也能获得合理的结果
        
        private String cpu = "Intel Core i5";          // 🔥 中端CPU,适合大多数用户
        private String memory = "8GB DDR4";            // 🔥 标准内存配置,满足日常需求
        private String storage = "256GB SSD";          // 🔥 SSD提供更好的性能体验
        private String graphics = "集成显卡";           // 🔥 集成显卡节省成本,适合办公
        private boolean wifi = true;                   // 🔥 现代电脑必备功能
        private boolean bluetooth = false;             // 🔥 可选功能,默认关闭节省电源
        
        public ComputerBuilder setCpu(String cpu) {
            this.cpu = cpu;
            return this;
        }
        
        public ComputerBuilder setMemory(String memory) {
            this.memory = memory;
            return this;
        }
        
        public ComputerBuilder setStorage(String storage) {
            this.storage = storage;
            return this;
        }
        
        public ComputerBuilder setGraphics(String graphics) {
            this.graphics = graphics;
            return this;
        }
        
        public ComputerBuilder setWifi(boolean wifi) {
            this.wifi = wifi;
            return this;
        }
        
        public ComputerBuilder setBluetooth(boolean bluetooth) {
            this.bluetooth = bluetooth;
            return this;
        }
        
        public Computer build() {
            // 验证必需的配置
            if (cpu == null || cpu.trim().isEmpty()) {
                throw new IllegalArgumentException("CPU配置不能为空");
            }
            
            return new Computer(this);
        }
    }
}

4.2 使用默认值的Builder

java 复制代码
public class ComputerExample {
    public static void main(String[] args) {
        // 使用默认配置,只修改需要的部分
        Computer basicComputer = new Computer.ComputerBuilder()
                .build();  // 直接使用默认值
        
        System.out.println("基础配置:" + basicComputer);
        
        // 高端配置,覆盖默认值
        Computer gamingComputer = new Computer.ComputerBuilder()
                .setCpu("Intel i7-12700K")
                .setMemory("32GB DDR4")
                .setStorage("1TB NVMe SSD")
                .setGraphics("RTX 4070")
                .setBluetooth(true)
                .build();
        
        System.out.println("游戏配置:" + gamingComputer);
        
        // 办公配置
        Computer officeComputer = new Computer.ComputerBuilder()
                .setCpu("Intel i5-12400")
                .setMemory("16GB DDR4")
                .setStorage("512GB SSD")
                .build();  // WiFi和蓝牙使用默认值
        
        System.out.println("办公配置:" + officeComputer);
    }
}

4.3 带验证的高级Builder

java 复制代码
/**
 * 用户注册信息 - 带完整验证的建造者
 */
public class User {
    private final String username;
    private final String email;
    private final String password;
    private final int age;
    private final String phone;
    
    private User(UserBuilder builder) {
        this.username = builder.username;
        this.email = builder.email;
        this.password = builder.password;
        this.age = builder.age;
        this.phone = builder.phone;
    }
    
    // getter方法...
    
    public static class UserBuilder {
        private String username;
        private String email;
        private String password;
        private int age;
        private String phone;
        
        public UserBuilder setUsername(String username) {
            this.username = username;
            return this;
        }
        
        public UserBuilder setEmail(String email) {
            this.email = email;
            return this;
        }
        
        public UserBuilder setPassword(String password) {
            this.password = password;
            return this;
        }
        
        public UserBuilder setAge(int age) {
            this.age = age;
            return this;
        }
        
        public UserBuilder setPhone(String phone) {
            this.phone = phone;
            return this;
        }
        
        public User build() {
            // 详细的验证逻辑
            validateUsername();
            validateEmail();
            validatePassword();
            validateAge();
            validatePhone();
            
            return new User(this);
        }
        
        private void validateUsername() {
            if (username == null || username.trim().isEmpty()) {
                throw new IllegalArgumentException("用户名不能为空");
            }
            if (username.length() < 3 || username.length() > 20) {
                throw new IllegalArgumentException("用户名长度必须在3-20个字符之间");
            }
        }
        
        private void validateEmail() {
            if (email == null || !email.contains("@")) {
                throw new IllegalArgumentException("邮箱格式不正确");
            }
        }
        
        private void validatePassword() {
            if (password == null || password.length() < 6) {
                throw new IllegalArgumentException("密码长度至少6位");
            }
        }
        
        private void validateAge() {
            if (age < 13 || age > 120) {
                throw new IllegalArgumentException("年龄必须在13-120之间");
            }
        }
        
        private void validatePhone() {
            if (phone != null && phone.length() != 11) {
                throw new IllegalArgumentException("手机号码必须是11位");
            }
        }
    }
}

5. 实际应用场景

5.1 HTTP请求构建

java 复制代码
/**
 * HTTP请求建造者 - 实际应用场景
 */
public class HttpRequest {
    private final String url;
    private final String method;
    private final Map<String, String> headers;
    private final String body;
    private final int timeout;
    
    private HttpRequest(HttpRequestBuilder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = new HashMap<>(builder.headers);
        this.body = builder.body;
        this.timeout = builder.timeout;
    }
    
    // getter方法...
    
    public static class HttpRequestBuilder {
        private String url;
        private String method = "GET";                    // 默认GET请求
        private Map<String, String> headers = new HashMap<>();
        private String body;
        private int timeout = 30000;                      // 默认30秒超时
        
        public HttpRequestBuilder url(String url) {
            this.url = url;
            return this;
        }
        
        public HttpRequestBuilder method(String method) {
            this.method = method;
            return this;
        }
        
        public HttpRequestBuilder header(String name, String value) {
            this.headers.put(name, value);
            return this;
        }
        
        public HttpRequestBuilder headers(Map<String, String> headers) {
            this.headers.putAll(headers);
            return this;
        }
        
        public HttpRequestBuilder body(String body) {
            this.body = body;
            return this;
        }
        
        public HttpRequestBuilder timeout(int timeout) {
            this.timeout = timeout;
            return this;
        }
        
        public HttpRequest build() {
            if (url == null || url.trim().isEmpty()) {
                throw new IllegalArgumentException("URL不能为空");
            }
            
            // POST请求必须有body
            if ("POST".equalsIgnoreCase(method) && body == null) {
                throw new IllegalArgumentException("POST请求必须包含请求体");
            }
            
            return new HttpRequest(this);
        }
    }
}

使用示例:

java 复制代码
public class HttpRequestExample {
    public static void main(String[] args) {
        // GET请求
        HttpRequest getRequest = new HttpRequest.HttpRequestBuilder()
                .url("https://api.example.com/users")
                .header("Authorization", "Bearer token123")
                .timeout(5000)
                .build();
        
        // POST请求
        HttpRequest postRequest = new HttpRequest.HttpRequestBuilder()
                .url("https://api.example.com/users")
                .method("POST")
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer token123")
                .body("{\"name\":\"张三\",\"age\":25}")
                .build();
    }
}

5.2 数据库连接配置

java 复制代码
/**
 * 数据库配置建造者
 */
public class DatabaseConfig {
    private final String host;
    private final int port;
    private final String database;
    private final String username;
    private final String password;
    private final int maxConnections;
    private final int timeout;
    private final boolean useSSL;
    
    private DatabaseConfig(DatabaseConfigBuilder builder) {
        this.host = builder.host;
        this.port = builder.port;
        this.database = builder.database;
        this.username = builder.username;
        this.password = builder.password;
        this.maxConnections = builder.maxConnections;
        this.timeout = builder.timeout;
        this.useSSL = builder.useSSL;
    }
    
    public static class DatabaseConfigBuilder {
        private String host = "localhost";           // 默认主机
        private int port = 3306;                    // 默认MySQL端口
        private String database;                    // 数据库名(必需)
        private String username;                    // 用户名(必需)
        private String password;                    // 密码(必需)
        private int maxConnections = 10;            // 默认最大连接数
        private int timeout = 30;                   // 默认超时时间(秒)
        private boolean useSSL = false;             // 默认不使用SSL
        
        public DatabaseConfigBuilder host(String host) {
            this.host = host;
            return this;
        }
        
        public DatabaseConfigBuilder port(int port) {
            this.port = port;
            return this;
        }
        
        public DatabaseConfigBuilder database(String database) {
            this.database = database;
            return this;
        }
        
        public DatabaseConfigBuilder username(String username) {
            this.username = username;
            return this;
        }
        
        public DatabaseConfigBuilder password(String password) {
            this.password = password;
            return this;
        }
        
        public DatabaseConfigBuilder maxConnections(int maxConnections) {
            this.maxConnections = maxConnections;
            return this;
        }
        
        public DatabaseConfigBuilder timeout(int timeout) {
            this.timeout = timeout;
            return this;
        }
        
        public DatabaseConfigBuilder useSSL(boolean useSSL) {
            this.useSSL = useSSL;
            return this;
        }
        
        public DatabaseConfig build() {
            // 验证必需的字段
            if (database == null || database.trim().isEmpty()) {
                throw new IllegalArgumentException("数据库名不能为空");
            }
            if (username == null || username.trim().isEmpty()) {
                throw new IllegalArgumentException("用户名不能为空");
            }
            if (password == null) {
                throw new IllegalArgumentException("密码不能为空");
            }
            
            // 验证端口号
            if (port < 1 || port > 65535) {
                throw new IllegalArgumentException("端口号必须在1-65535之间");
            }
            
            return new DatabaseConfig(this);
        }
    }
}

6. 与其他方式的对比

6.1 VS 构造函数

java 复制代码
// ❌ 构造函数方式
public class PersonBad {
    private String name;
    private int age;
    private String email;
    private String phone;
    private String address;
    
    // 参数太多,容易搞错
    public PersonBad(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;
    }
    
    // 需要很多重载构造函数
    public PersonBad(String name, int age) { ... }
    public PersonBad(String name, int age, String email) { ... }
    public PersonBad(String name, int age, String email, String phone) { ... }
}

// 使用时容易出错
PersonBad person = new PersonBad("张三", 25, "123456789", "zhang@example.com", "北京");
//                                      ↑ 这两个参数顺序搞错了!

// ✅ 建造者方式
PersonGood person = new PersonGood.Builder()
    .setName("张三")
    .setAge(25)
    .setEmail("zhang@example.com")    // 清楚知道在设置什么
    .setPhone("123456789")
    .setAddress("北京")
    .build();

6.2 VS Setter方法

java 复制代码
// ❌ Setter方式
public class PersonSetter {
    private String name;
    private int age;
    private String email;
    
    // 提供setter方法
    public void setName(String name) { this.name = name; }
    public void setAge(int age) { this.age = age; }
    public void setEmail(String email) { this.email = email; }
}

// 使用时的问题
PersonSetter person = new PersonSetter();
person.setName("张三");
person.setAge(25);
// 忘记设置email了!
// 而且对象创建后还可以被修改,不安全

// 在其他地方
person.setAge(30);  // 对象被意外修改了!

// ✅ 建造者方式
PersonGood person = new PersonGood.Builder()
    .setName("张三")
    .setAge(25)
    .setEmail("zhang@example.com")
    .build();  // 创建后就不能修改了,更安全

6.3 VS 工厂模式

java 复制代码
// 工厂模式:用于创建不同类型的对象
public class ComputerFactory {
    public static Computer createGamingComputer() {
        return new Computer("Intel i7", "32GB", "RTX 4070");
    }
    
    public static Computer createOfficeComputer() {
        return new Computer("Intel i5", "16GB", "集成显卡");
    }
}

// 建造者模式:用于灵活配置同一类型的对象
Computer computer = new Computer.Builder()
    .setCpu("Intel i7")      // 可以自由组合配置
    .setMemory("32GB")
    .setGraphics("RTX 4070")
    .build();

7. 总结

7.1 建造者模式的优点

  1. 代码可读性强:每个方法名都说明了在设置什么
  2. 参数验证集中 :所有验证逻辑都在build()方法中
  3. 灵活性高:可以自由组合不同的配置
  4. 不可变对象:创建后对象不能修改,更安全
  5. 默认值支持:可以为可选参数提供合理的默认值

7.2 适用场景

建造者模式特别适合以下情况:

  • 参数很多(通常超过4个)
  • 有可选参数
  • 需要验证参数的组合
  • 希望创建不可变对象
  • 配置类、请求类、复杂实体类

7.3 实现要点

  1. 私有构造器:确保只能通过Builder创建对象
  2. 静态内部Builder类:方便使用,且能访问外部类的私有构造器
  3. 链式调用 :每个setter方法都返回this
  4. final字段:确保对象不可变
  5. build()方法验证:在创建对象前进行完整性检查

7.4 注意事项

  1. 代码量增加:需要写更多的代码
  2. 内存开销:Builder对象需要额外的内存
  3. 简单对象不适用:参数很少的对象直接用构造器更简单

7.5 实践建议

  1. 方法命名统一 :统一使用setXxx()withXxx()
  2. 验证逻辑合理 :在build()方法中进行必要的验证
  3. 提供默认值:为可选参数设置合理的默认值
  4. 文档完善:为Builder的每个方法写清楚注释

建造者模式是一个非常实用的设计模式,特别适合创建复杂的配置对象。掌握了它,你的代码会变得更清晰、更安全、更易维护

相关推荐
大猫和小黄2 小时前
若依从零到部署:前后端分离和微服务版
java·微服务·云原生·架构·前后端分离·若依
Geoking.2 小时前
【设计模式】享元模式(Flyweight)详解:用共享对象对抗内存爆炸
java·设计模式·享元模式
callJJ2 小时前
Spring设计模式与依赖注入详解
java·spring·设计模式·idea·工厂模式
ExiFengs2 小时前
Java使用策略模式实现多实体通用操作的优雅设计
java·开发语言·设计模式·策略模式
茶本无香2 小时前
设计模式之三—工厂模式:灵活对象创建的艺术
java·开发语言·设计模式·工厂模式
week_泽2 小时前
第7课:管理长期记忆的关键架构决策 - 学习笔记_7
java·笔记·学习·ai agent
爱装代码的小瓶子2 小时前
【c++进阶】c++11下类的新变化以及Lambda函数和封装器
java·开发语言·c++
乌萨奇也要立志学C++2 小时前
【Linux】线程同步 条件变量精讲 + 生产者消费者模型完整实现
java·linux·运维
澄澈青空~2 小时前
病毒木马侵入系统内核的底层运作机理
java·linux·服务器