二十三种设计模式(五)--建造者模式

建造者模式

建造者模式解决的是构建大型复杂的类对象的问题.

当我们定义了一个简单的类, 构造函数非常简单, 直接new即可

但是当构建参数非常多, 比如有几十上百个, 且并不是所有参数都能用得到, 我们实际用到的仅仅是将某一部分参数初始化的类对象, 此时这个构建的过程将会非常的复杂繁琐

为了解决这种通过不同的参数组合实例化不同的类的问题, 就有了建造者模式

建造者模式的实现方式非常多, 但核心思想是将
XX obj = new XXClass(param1, param2, ... ...)

这个实例化过程拆解为:
XX obj = new Builder().a().b().build()

这样使用一个构建器创建实例的过程

建造者模式本质上就是把"对象的构建过程"从"对象本身"拆出来,由一个 Builder 专门负责构建,再由 build() 返回最终对象

把构造函数拆成一步步可读、可控、可组合的链式调用。

以下是一个最精简的建造者模式实现:

java 复制代码
public class BuilderMode {
    public static void main(String[] args) {
        Product product1 = new Product.Builder().b(5).build();
        product1.run();

        Product product2 = new Product.Builder().b(10).c(15).build();
        product2.run();
    }
}

class Product {
    private int a = 12; // 默认固定参数
    private int b;
    private int c;

    public void run() {
        System.out.print("Product a = " + a);
        if (b != 0) {
            System.out.print(" b = " + b);
        }
        if (c != 0) {
            System.out.print(" c = " + c);
        }
        System.out.println("");
    }

    // ***构建器***
    // 构建器采用内部类的方式实现
    // 这样可以将封装性做的很好, 避免Product类对外暴露属性
    public static class Builder {
        int b = 0;
        int c = 0;

        Builder b(int b) {
            this.b = b;
            return this;
        }

        Builder c(int c) {
            this.c = c;
            return this;
        }

        Product build() {
            // 参数合法性验证及异常处理
            // 处理异常过程省略

            // 构建最终合法可运行的对象
            Product product = new Product();
            product.b = b;
            product.c = c;
            return product;
        }
    }
}

运行结果:

复制代码
Product a = 12 b = 5
Product a = 12 b = 10 c = 15

下面是一个符合 Effective Java 推荐风格、贴近生产环境的建造者模式实现,涵盖空值校验、不可变性、链式调用、默认值、Builder 内部类、流畅 API 等核心特性的建造者模式类实现:

java 复制代码
final class User {
    // 核心字段(不可变 + 私有final)
    private final Long id;                // 必填:用户ID
    private final String username;        // 必填:用户名
    private final String email;           // 必填:邮箱
    private final Integer age;            // 可选:年龄(默认18)
    private final String phone;           // 可选:手机号(默认null)
    private final List<String> roles;     // 可选:角色列表(可变类型,需防御性拷贝)
    private final LocalDateTime createTime; // 可选:创建时间(默认当前时间)

    // 私有构造器:仅内部Builder可调用
    private User(Builder builder) {
        // 1. 必填参数校验(Effective Java强调:尽早校验参数)
        this.id = Objects.requireNonNull(builder.id, "用户ID(id)不能为空");
        this.username = Objects.requireNonNull(builder.username, "用户名(username)不能为空");
        this.email = Objects.requireNonNull(builder.email, "邮箱(email)不能为空");

        // 2. 可选参数(带默认值)
        this.age = builder.age != null ? builder.age : 18;
        this.phone = builder.phone;
        this.createTime = builder.createTime != null ? builder.createTime : LocalDateTime.now();

        // 3. 可变类型防御性拷贝(避免外部修改内部状态)
        this.roles = builder.roles != null
                ? Collections.unmodifiableList(new ArrayList<>(builder.roles)) // 不可变列表
                : Collections.emptyList();
    }

    // 静态内部Builder类(Effective Java推荐:静态内部类形式)
    public static class Builder {
        // 与User完全一致的字段(非final,用于临时存储)
        private Long id;
        private String username;
        private String email;
        private Integer age;
        private String phone;
        private List<String> roles;
        private LocalDateTime createTime;

        // 链式setter:返回Builder本身,支持流畅调用
        public Builder id(Long id) {
            this.id = id;
            return this;
        }

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

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

        public Builder age(Integer age) {
            // 字段合法性校验(非空+业务规则)
            if (age != null && (age < 0 || age > 150)) {
                throw new IllegalArgumentException("年龄(age)必须在0-150之间");
            }
            this.age = age;
            return this;
        }

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

        public Builder roles(List<String> roles) {
            this.roles = roles;
            return this;
        }

        public Builder createTime(LocalDateTime createTime) {
            this.createTime = createTime;
            return this;
        }

        // 核心build方法:创建并返回不可变User对象
        public User build() {
            return new User(this);
        }
    }

    // 所有字段的getter(无setter,保证不可变性)
    public Long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public String getEmail() {
        return email;
    }

    public Integer getAge() {
        return age;
    }

    public String getPhone() {
        return phone;
    }

    // 返回不可变视图,防止外部修改
    public List<String> getRoles() {
        return roles;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    // 重写equals/hashCode(生产环境必备)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) &&
                Objects.equals(username, user.username) &&
                Objects.equals(email, user.email) &&
                Objects.equals(age, user.age) &&
                Objects.equals(phone, user.phone) &&
                Objects.equals(roles, user.roles) &&
                Objects.equals(createTime, user.createTime);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, email, age, phone, roles, createTime);
    }

    // 重写toString(调试/日志必备)
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                ", phone='" + phone + '\'' +
                ", roles=" + roles +
                ", createTime=" + createTime +
                '}';
    }
}
  1. 参数验证分布在builder的每个字段设置时, 便于及时发现问题
  2. User类的所有属性都是final private权限, 只能通过内部构造器赋值
  3. 在build阶段校验参数非空等业务逻辑, 保证最终生成的对象正确可运行
  4. 构造器对默认值进行处理, 保证对象合法可用
  5. 对可变类型(如 List、Date)做拷贝,避免外部修改

测试:

java 复制代码
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Collections;
import java.util.ArrayList;

public class BuilderMode {
    public static void main(String[] args) {
        // 1. 构建完整参数的User
        User fullUser = new User.Builder()
                .id(1001L)
                .username("zhangsan")
                .email("zhangsan@example.com")
                .age(28)
                .phone("13800138000")
                .roles(List.of("admin", "user"))
                .createTime(LocalDateTime.of(2025, 1, 1, 10, 0))
                .build();
        System.out.println("完整参数User: " + fullUser);

        // 2. 构建仅必填参数的User(使用默认值)
        User minimalUser = new User.Builder()
                .id(1002L)
                .username("lisi")
                .email("lisi@example.com")
                .build();
        System.out.println("仅必填参数User: " + minimalUser);

        // 3. 测试参数校验(年龄非法)
        try {
            new User.Builder()
                    .id(1003L)
                    .username("wangwu")
                    .email("wangwu@example.com")
                    .age(200) // 非法年龄
                    .build();
        } catch (IllegalArgumentException e) {
            System.out.println("参数校验生效: " + e.getMessage());
        }

        // 4. 测试不可变性(尝试修改roles)
        try {
            fullUser.getRoles().add("guest"); // 不可变列表会抛异常
        } catch (UnsupportedOperationException e) {
            System.out.println("不可变性校验生效: " + e.getMessage());
        }
    }
}

运行结果:

复制代码
完整参数User: User{id=1001, username='zhangsan', email='zhangsan@example.com', age=28, phone='13800138000', roles=[admin, user], createTime=2025-01-01T10:00}
仅必填参数User: User{id=1002, username='lisi', email='lisi@example.com', age=18, phone='null', roles=[], createTime=2025-11-30T13:14:48.996069700}
参数校验生效: 年龄(age)必须在0-150之间
不可变性校验生效: null
关于GoF经典的建造和模式

经典建造者模式有如下几个角色组成:

Builder 抽象接口, 用来制定构建器的标准接口, 当有多个构建器时才会用到

ConcreteBuilder 构建器的具体实现, 生成实际的产品对象

Director 指挥家, 用来固定构建器的构建流程

Product 产品类, 包含最终所有的属性和方法, 但是只能由构建器实例化, 外部不能实例化

Client 调用端, 先获取构造器实例, 然后通过构造器创建产品实例对象.

经典建造者模式在实际生产过程中很少用到, 不再举例.

相关推荐
小羊没烦恼!1 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里1 天前
java中的继承和多态的区别
java
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕1 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码1 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖1 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时1 天前
01-06-A-JVM排查实战详解
java·jvm
vipxieliang1 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
ba_pi1 天前
mysql 查询所有表名并授权
java
ProcessOn官方账号1 天前
java函数式编程--入门基础
java·编程·函数式编程