建造者模式:告别“叠罗汉”式构造器,优雅搭建复杂对象

2025/09/30 Design Patterns 共 7104 字,约 21 分钟

建造者模式:一步一步搭建复杂对象

在软件开发中,我们经常会遇到需要创建复杂对象的场景。这些对象可能包含多个属性,其中一些是必需的,另一些是可选的,而且构建过程可能涉及多个步骤。传统的构造方法在面对这种情况时往往显得力不从心,要么导致构造函数参数列表过长,要么使得客户端代码难以理解和维护。建造者模式正是为了解决这些问题而生的。

为什么需要建造者模式?

传统构造方式的痛点

假设我们需要构建一个User类,它包含以下属性:

public class User {
    private final String firstName;    // 必需
    private final String lastName;     // 必需
    private final int age;             // 可选
    private final String phone;        // 可选
    private final String address;      // 可选
    private final String email;        // 可选
    
    // 构造函数...
}

使用传统的构造方法,我们会面临以下问题:

1. 伸缩构造函数模式(Telescoping Constructor Pattern)

public User(String firstName, String lastName) {
    this(firstName, lastName, 0);
}

public User(String firstName, String lastName, int age) {
    this(firstName, lastName, age, "");
}

public User(String firstName, String lastName, int age, String phone) {
    this(firstName, lastName, age, phone, "");
}

// 更多构造函数...

这种方式虽然可行,但当参数增多时,构造函数数量会呈指数级增长,而且客户端代码很难弄清楚每个参数的含义。

2. JavaBean模式

public class User {
    private String firstName;
    private String lastName;
    private int age;
    private String phone;
    private String address;
    private String email;
    
    // 无参构造函数 + setter方法
    public User() {}
    
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    
    // 其他setter方法...
}

这种方式虽然简单,但失去了不可变性(对象状态可能在构建后被修改),而且构建过程被分成了多个步骤,无法保证对象在构建过程中的一致性。

建造者模式的核心思想

建造者模式将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。它通过一个指导者(Director)来指导构建过程,通过具体的建造者(Concrete Builder)来实现不同表示的构建。

模式结构

  • Product(产品):要创建的复杂对象
  • Builder(建造者):抽象接口,定义创建产品各个部件的操作
  • ConcreteBuilder(具体建造者):实现Builder接口,构造和装配各个部件
  • Director(指导者):构造一个使用Builder接口的对象

建造者模式的实现

基础实现

让我们用建造者模式重新设计User类:

public class User {
    private final String firstName;
    private final String lastName;
    private final int age;
    private final String phone;
    private final String address;
    private final String email;
    
    // 私有构造函数,只能通过Builder创建
    private User(UserBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
        this.email = builder.email;
    }
    
    // 静态内部类 Builder
    public static class UserBuilder {
        // 必需参数
        private final String firstName;
        private final String lastName;
        
        // 可选参数 - 初始化为默认值
        private int age = 0;
        private String phone = "";
        private String address = "";
        private String email = "";
        
        public UserBuilder(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
        
        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }
        
        public UserBuilder phone(String phone) {
            this.phone = phone;
            return this;
        }
        
        public UserBuilder address(String address) {
            this.address = address;
            return this;
        }
        
        public UserBuilder email(String email) {
            this.email = email;
            return this;
        }
        
        // 构建方法
        public User build() {
            return new User(this);
        }
    }
    
    // getter方法
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public int getAge() { return age; }
    public String getPhone() { return phone; }
    public String getAddress() { return address; }
    public String getEmail() { return email; }
    
    @Override
    public String toString() {
        return "User{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                ", address='" + address + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

客户端使用方式

public class BuilderPatternDemo {
    public static void main(String[] args) {
        // 创建User对象 - 只设置必需参数
        User user1 = new User.UserBuilder("张", "三").build();
        
        // 创建User对象 - 设置部分可选参数
        User user2 = new User.UserBuilder("李", "四")
                .age(25)
                .phone("13800138000")
                .build();
        
        // 创建User对象 - 设置所有参数
        User user3 = new User.UserBuilder("王", "五")
                .age(30)
                .phone("13900139000")
                .address("北京市朝阳区")
                .email("wangwu@example.com")
                .build();
        
        System.out.println(user1);
        System.out.println(user2);
        System.out.println(user3);
    }
}

带验证的建造者模式

在实际应用中,我们通常需要在构建过程中进行参数验证:

public static class UserBuilder {
    private final String firstName;
    private final String lastName;
    private int age = 0;
    private String phone = "";
    private String address = "";
    private String email = "";
    
    public UserBuilder(String firstName, String lastName) {
        if (firstName == null || firstName.trim().isEmpty()) {
            throw new IllegalArgumentException("firstName不能为空");
        }
        if (lastName == null || lastName.trim().isEmpty()) {
            throw new IllegalArgumentException("lastName不能为空");
        }
        this.firstName = firstName;
        this.lastName = lastName;
    }
    
    public UserBuilder age(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄必须在0-150之间");
        }
        this.age = age;
        return this;
    }
    
    public UserBuilder phone(String phone) {
        if (phone != null && !phone.matches("\\d{11}")) {
            throw new IllegalArgumentException("手机号格式不正确");
        }
        this.phone = phone;
        return this;
    }
    
    public User build() {
        // 构建前的最终验证
        if (age < 18 && email == null) {
            throw new IllegalStateException("未成年人必须提供邮箱");
        }
        return new User(this);
    }
}

实际应用场景

1. 配置对象的构建

在框架开发中,建造者模式常用于构建配置对象:

// 数据库配置
DataSourceConfig config = new DataSourceConfig.Builder()
    .url("jdbc:mysql://localhost:3306/test")
    .username("root")
    .password("password")
    .maxPoolSize(20)
    .minPoolSize(5)
    .connectionTimeout(30000)
    .build();

2. 复杂查询构建

在构建复杂查询时,建造者模式特别有用:

// SQL查询构建
Query query = new QueryBuilder()
    .select("id", "name", "email")
    .from("users")
    .where("age > ?", 18)
    .and("status = ?", "ACTIVE")
    .orderBy("created_at", "DESC")
    .limit(10)
    .offset(0)
    .build();

3. HTTP请求构建

// HTTP请求构建
HttpRequest request = new HttpRequestBuilder()
    .url("https://api.example.com/users")
    .method("POST")
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer token")
    .body("{\"name\":\"张三\",\"age\":25}")
    .timeout(5000)
    .build();

在开源框架中的应用

Lombok的@Builder注解

Lombok提供了@Builder注解,可以自动生成建造者模式的代码:

import lombok.Builder;
import lombok.ToString;

@Builder
@ToString
public class User {
    private final String firstName;
    private final String lastName;
    private final int age;
    private final String phone;
    private final String address;
    private final String email;
}

// 使用方式
User user = User.builder()
    .firstName("张")
    .lastName("三")
    .age(25)
    .phone("13800138000")
    .build();

StringBuilder和StringBuffer

Java标准库中的StringBuilderStringBuffer就是建造者模式的典型应用:

StringBuilder sb = new StringBuilder();
String result = sb.append("Hello")
                 .append(" ")
                 .append("World")
                 .append("!")
                 .toString();

建造者模式的变体

1. 流式接口(Fluent Interface)

通过方法链式调用,让代码更加流畅易读:

public class Pizza {
    private String size;
    private boolean cheese;
    private boolean pepperoni;
    private boolean bacon;
    
    public static class Builder {
        private String size;
        private boolean cheese = false;
        private boolean pepperoni = false;
        private boolean bacon = false;
        
        public Builder(String size) {
            this.size = size;
        }
        
        public Builder addCheese() {
            this.cheese = true;
            return this;
        }
        
        public Builder addPepperoni() {
            this.pepperoni = true;
            return this;
        }
        
        public Builder addBacon() {
            this.bacon = true;
            return this;
        }
        
        public Pizza build() {
            return new Pizza(this);
        }
    }
    
    private Pizza(Builder builder) {
        this.size = builder.size;
        this.cheese = builder.cheese;
        this.pepperoni = builder.pepperoni;
        this.bacon = builder.bacon;
    }
}

// 使用
Pizza pizza = new Pizza.Builder("Large")
    .addCheese()
    .addPepperoni()
    .addBacon()
    .build();

2. 带指导者的建造者模式

当构建过程特别复杂时,可以引入指导者角色:

// 指导者
public class UserDirector {
    public User constructAdultUser(UserBuilder builder, String firstName, String lastName) {
        return builder.firstName(firstName)
                     .lastName(lastName)
                     .age(18)
                     .email(firstName + "." + lastName + "@example.com")
                     .build();
    }
    
    public User constructSeniorUser(UserBuilder builder, String firstName, String lastName) {
        return builder.firstName(firstName)
                     .lastName(lastName)
                     .age(65)
                     .phone("400-000-0000")
                     .address("养老院")
                     .build();
    }
}

建造者模式的优缺点

优点

  1. 封装性好:建造者模式将复杂对象的构建过程封装起来,客户端不需要知道内部细节
  2. 易于扩展:当需要构建不同表示的对象时,只需要增加相应的具体建造者
  3. 便于控制构建过程:指导者类可以精细控制构建过程
  4. 提高代码可读性:方法链式调用让代码更加清晰易读
  5. 保证对象不可变性:构建完成后对象状态不可修改

缺点

  1. 代码复杂度增加:需要创建多个类,代码量增加
  2. 适用范围有限:主要用于创建复杂对象,简单对象使用建造者模式反而会增加复杂度
  3. 产品需要有共同点:如果产品之间差异很大,建造者模式就不太适用

总结

建造者模式是创建复杂对象的利器,它通过将构建过程与表示分离,提供了灵活、可读性强的对象创建方式。虽然会增加一些代码复杂度,但在面对包含多个可选参数的复杂对象时,这种投入是值得的。

在实际开发中,当遇到以下情况时,考虑使用建造者模式:

  • 对象包含大量属性,其中很多是可选的
  • 对象的构建过程复杂,涉及多个步骤
  • 需要创建不同表示的对象,但构建过程相似
  • 希望创建不可变对象

通过合理运用建造者模式,我们可以编写出更加健壮、可维护的代码,让复杂对象的创建变得简单而优雅。

文档信息

Search

    Table of Contents