尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

你的构造参数越写越长——不是类设计错了,是该用 Builder 模式了

你的构造参数越写越长——不是类设计错了,是该用 Builder 模式了 我见过一个类构造函数长这样java public OrderQuery( Long userId, Integer status, Date startTime, Date endTime, String keyword, Integer page, Integer pageSize, String sortField, Boolean desc, ListLong tagIds ) { // ... }十个参数还不算最坏的情况。调用的时候更痛苦java new OrderQuery(userId, null, null, null, 手机, 1, 20, createTime, true, null);你盯着这行代码根本不知道哪个 null 对应什么。这种代码不是业务复杂是构造方式出了问题。构造函数参数爆炸是怎么来的参数多通常有两种原因。一种是对象本身确实需要很多状态比如查询条件、配置对象。另一种是设计者把可选和必须混在一起全部塞进构造函数。后者的危害更大。因为构造函数要求调用者按固定顺序填值少一个就编译不过。为了应对可选参数团队会开始写重载java public OrderQuery(Long userId) public OrderQuery(Long userId, Integer status) public OrderQuery(Long userId, Integer status, Date startTime)这就是经典的 telescoping constructor 反模式。每增加一个可选参数重载数量可能翻倍。代码没变薄反而更难维护。Builder 模式解决的不是参数多很多人以为 Builder 模式就是为了让链式调用好看。这个理解太浅。Builder 真正解决的是命名调用和渐进构造的问题。命名调用意味着你不再靠位置猜参数而是靠名字java OrderQuery query OrderQuery.builder() .userId(userId) .keyword(手机) .page(1) .pageSize(20) .sortField(createTime) .desc(true) .build();每一个.xxx()都是自解释的。你不需要回到类定义里数参数位置。渐进构造意味着对象可以分阶段组装。有些字段现在知道有些字段稍后填充。这在构造复杂对象时特别重要比如一个 HTTP 请求对象header、body、timeout 可能来自不同的地方。但 Builder 不是万能药我见过有人给只有两三个必填参数的类也加 Builder。那是过度设计。Builder 适合的场景有三个信号可选参数超过三个且组合方式多变对象构造完成后应该不可变不允许后续 setter 修改构造逻辑复杂需要校验或转换。如果只是一个简单的值对象两三个字段直接写构造函数加 final 字段就够了。不要为了炫技引入 Builder。另一个常见误区是把 Builder 当成 Factory 用。Factory 解决的是创建哪个具体类的问题Builder 解决的是同一个类怎么一步步组装的问题。前者做选择后者做装配。一个真实的踩坑Builder 里的校验不要拖到 build() 里才发现有次我们用一个 Builder 构造消息通知对象。必填字段是templateId和receiverId但 Builder 没有在设置时校验只是拖到build()方法里统一检查。结果一个粗心的同事在链式调用里漏了.receiverId()代码编译通过运行到build()才抛异常。更糟的是这个对象是在异步任务里构造的异常被吞掉消息根本没发出去。后来我总结了一条规则必填字段在 Builder 里用强制方法体现可选字段用带默认值或延迟校验。如果某个字段真的缺一不可就在build()里抛IllegalStateException并且异常信息要告诉用户缺了哪个字段。现代 Java 里的 Builder手写还是 LombokLombok 的Builder很方便但有个坑——它默认把所有字段都放进 Builder包括那些你本来想隐藏的字段。比如一个领域对象有id和createdAt两个数据库自动填充的字段。如果直接用Builder调用方可能会写.id(123L)构造出一个违反业务规则的对象。我们的做法是在领域对象上不用Builder而是在需要构建的场景里写一个专门的 Builder 类或静态工厂只暴露允许外部设置的字段。Lombok 适合 DTO、查询条件这类扁平对象不适合领域模型。Builder 模式的本质Builder 模式把对象是什么和对象怎么造出来拆开了。构造函数表达的是对象最终状态你给我这些参数我给你一个完整对象。Builder 表达的是构造过程你可以分步骤、按名字、按需要组合。当你的对象构造过程本身就有复杂度时就该考虑 Builder。否则你写的不是构造逻辑是在跟编译器和调用者打游击。我在做一个用卡皮巴拉讲设计模式的微信小程序「爪爪代码冒险记」23 个设计模式用漫画 答题的方式讲目前正在开发中。如果你觉得这类内容有意思搜一下「爪爪代码冒险记」或者等我后面的文章。
返回列表