为什么使用SpringAI时通常用Builder来创建对象?详解 【Builder模式】和【直接 new】的区别

SpringAI的场景

ChatClient

ChatClient提供了create方法和Builder模式来创建对象。create方法就可以看作是一个静态工厂方法,适合快速创建一个能用的ChatClient,Builder模式则支持更加精细的管理。

ChatClient是一个接口,其具体实现如DefaultChatClient倒也可以直接new,只不过要传入的是一个DefaultChatClientRequestSpec类,相当复杂。

Advisor

在Advisor中,它们的构造函数大多是私有的,只能通过Builder来创建不同种类的Advisor。图例为ChatMemoryAdvisor

Builder 模式和直接new对象是创建对象的两种不同方式,它们在代码可读性、灵活性、扩展性等方面存在显著区别,具体如下:

构造参数的处理

  • 直接new :如果类的构造函数参数较多(尤其是存在可选参数时),直接new会导致参数列表冗长,且参数顺序容易混淆,可读性差。例如:

    复制代码
    // 直接new,参数多且顺序易出错
    User user = new User("张三", 25, "北京", "13800138000", "zhangsan@xxx.com");

    若参数中有多个同类型值(如多个字符串),很容易传错顺序,且难以直观区分每个参数的含义。

  • Builder 模式 :通过链式调用设置参数,每个参数的含义清晰 ,支持可选参数灵活配置,无需传递无意义的默认值。例如:

    复制代码
    // Builder模式,参数清晰,可选参数按需设置
    User user = new User.Builder()
        .name("张三")
        .age(25)
        .address("北京")
        .build();

    即使参数很多,也能通过方法名明确每个参数的作用,避免混淆。

对象的不可变性

  • 直接new :若类提供setter方法,对象创建后状态仍可被修改,无法保证不可变性 ;若不提供setter,则必须在构造时传入所有参数(包括可选参数),灵活性差。

  • Builder 模式 :通常配合不可变类 设计:Builder 内部负责接收参数,最终通过build()方法创建不可变对象(对象无setter,属性为final)。例如:

    复制代码
    public class User {
        private final String name;
        private final int age;
        private final String address;
    
        private User(Builder builder) {
            this.name = builder.name;
            this.age = builder.age;
            this.address = builder.address;
        }
    
        // 无setter,保证不可变
        public static class Builder {
            private String name;
            private int age;
            private String address;
    
            public Builder name(String name) {
                this.name = name;
                return this;
            }
            // ...其他setter方法
            public User build() {
                return new User(this);
            }
        }
    }

    这样创建的User对象一旦生成,状态就无法修改,线程安全性更高。

复杂对象的构建逻辑

  • 直接new :仅能创建简单对象,若对象包含复杂的组件依赖或初始化逻辑(如对象 A 依赖对象 B 和 C,且 B、C 也需要复杂初始化),直接new会导致代码耦合严重,逻辑分散。

  • Builder 模式 :可将复杂对象的构建逻辑封装在 Builder 中,分离 "对象表示" 和 "构建过程"。例如创建一个包含多个组件的Computer对象:

    复制代码
    Computer computer = new Computer.Builder()
        .cpu("Intel i9")
        .memory("32GB")
        .disk("1TB SSD")
        .gpu("RTX 4090") // 可选组件
        .build();

    Builder 内部可处理组件的依赖关系(如必须有 CPU 和内存,GPU 可选),甚至在build()中校验参数合法性(如内存大小不能为 0)。

扩展性

  • 直接new :若需要新增参数,必须修改构造函数,违反 "开闭原则",且所有调用new的地方都需调整,维护成本高。

  • Builder 模式 :新增参数时,只需在 Builder 中添加对应的设置方法,原有的build()逻辑和调用代码无需修改,扩展性更好。例如新增phone参数:

    复制代码
    public static class Builder {
        // ...原有属性
        private String phone;
    
        public Builder phone(String phone) {
            this.phone = phone;
            return this;
        }
    
        // build()无需修改
    }

    调用方按需选择是否设置phone,不影响旧代码。

使用场景

  • 直接new :适用于简单对象(参数少、无复杂逻辑),如:

    复制代码
    List<String> list = new ArrayList<>();
  • Builder 模式 :适用于复杂对象(参数多、有可选参数、需不可变性或复杂构建逻辑),如:

    • 框架中的配置对象(如 OkHttp 的OkHttpClient.Builder);
    • 数据库查询条件构建(如 MyBatis 的SqlSessionFactoryBuilder);
    • 需保证不可变的领域对象(如订单、用户信息)。

总结

特性 直接new Builder 模式
参数可读性 差(参数多易混淆) 好(链式调用,参数名明确)
对象不可变性 难保证(需无 setter + 全参构造) 易保证(通过 Builder 构建不可变对象)
复杂构建逻辑 难以封装 可封装在 Builder 中,逻辑清晰
扩展性 差(修改构造函数影响所有调用) 好(新增参数不影响旧代码)
适用场景 简单对象 复杂对象、不可变对象、多参数对象

Builder 模式通过封装构建过程链式调用 解决了直接new的痛点,但会增加少量代码量(需定义 Builder 类)。在实际开发中,应根据对象复杂度选择合适的创建方式。

相关推荐
阿杰真不会敲代码40 分钟前
Filter与Interceptor深度解析:分清这两个“拦截器”,面试不再掉坑
java·spring boot·面试
青瓷程序设计1 小时前
【宠物识别系统】Python+TensorFlow+Vue3+Django+人工智能+深度学习+卷积神经网络算法
人工智能·python·深度学习
带刺的坐椅1 小时前
Solon AI 开发学习6 - chat - 两种 http 流式输入输出
java·ai·solon
客梦1 小时前
Java 道路信息系统
java·笔记
诸神缄默不语2 小时前
Python 3中的win32com使用教程+示例:从Excel读取数据生成Word格式报告批量发邮件
python·word·excel
k***1952 小时前
Tomcat的升级
java·tomcat
j***49563 小时前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
草莓熊Lotso3 小时前
unordered_map/unordered_set 使用指南:差异、性能与场景选择
java·开发语言·c++·人工智能·经验分享·python·网络协议