建造者模式:链式调用、Builder与构造器区别、Lombok @Builder、C++实现
大家好,我是你们的老朋友。今天我们来聊聊建造者模式。说实话,这个模式是我在实际项目中用得最频繁的模式之一。为什么?因为它解决了一个很实在的问题——当你的对象构造参数太多、太复杂时,代码会变得很难看。
你想想看,一个类有七八个字段,有些还是可选的。用构造器重载?那得写多少个重载版本?用setter?对象创建和设置分开,中间状态可能不完整。嗯,这时候建造者模式就派上用场了。
什么是建造者模式?
说白了,建造者模式就是把对象的构建过程和它的表示分离开。你一步步地告诉建造者你要什么,最后它给你一个完整的对象。就像去餐厅点餐——你告诉服务员要什么配料,最后厨房给你端出一份完整的汉堡。
我个人习惯把建造者模式理解为「分步构造,一步到位」。它特别适合那些构造参数多、且很多参数有默认值的场景。
核心思想:将复杂对象的构建过程抽象出来,让同样的构建过程可以创建不同的表示。
Builder与构造器的区别
我记得刚入行时,有个同事问我:「直接用构造器不就行了?干嘛要搞个Builder?」这个问题问得好。我们来对比一下:
| 对比维度 | 构造器 | Builder |
|---|---|---|
| 参数数量 | 参数少时好用,多了就乱 | 参数再多也清晰 |
| 可选参数 | 需要大量重载 | 天然支持 |
| 可读性 | new Person("张三", 25, "男", ...) 不知道每个参数含义 | Person.builder().name("张三").age(25).gender("男").build() 一目了然 |
| 不可变对象 | 支持(final字段) | 支持(Builder构建后对象不可变) |
| 代码量 | 少 | 多(但Lombok可以自动生成) |
我曾经在一个项目中接手过一个有15个参数的构造器。每次new对象时,都要数着参数位置,生怕传错。后来我重构成了Builder模式,代码瞬间清爽了。避坑指南:如果你发现构造器参数超过4个,就该考虑用Builder了。
链式调用
链式调用是Builder模式最吸引人的地方。每个setter方法都返回this,这样你就可以连续调用:
// Java示例
User user = new User.Builder()
.username("admin")
.password("123456")
.email("admin@example.com")
.phone("13800138000")
.build();
为什么大家喜欢链式调用?因为它符合人的阅读习惯——从上到下,一步步构建。而且IDE的自动补全功能会提示你下一步可以调什么方法,写起来很流畅。
小技巧:链式调用中,每个方法的返回值类型要写对。如果方法返回Builder本身,用return this;如果是子类Builder,要返回子类类型。
Lombok @Builder
说实话,手动写Builder代码挺烦的。尤其是那些字段多的类,Builder代码比业务代码还长。好在Lombok提供了@Builder注解,一行搞定:
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class User {
private String username;
private String password;
private String email;
private String phone;
private Integer age;
}
// 使用
User user = User.builder()
.username("admin")
.password("123456")
.email("admin@example.com")
.build();
System.out.println(user);
Lombok的@Builder会帮你生成一个静态内部Builder类,包含所有字段的setter方法和一个build()方法。我在项目中几乎都用这个,省时省力。但要注意一点:如果你需要自定义构建逻辑(比如字段校验),还是得手写Builder。
注意:Lombok的@Builder默认生成全参构造器。如果你需要无参构造器,可以加上@NoArgsConstructor和@AllArgsConstructor。另外,@Builder和@Setter一起用时要注意,Builder的setter方法和普通setter方法可能会冲突。
C++实现
Java有Lombok,C++可没有。但C++的Builder模式实现起来也很优雅。我个人习惯用静态内部类的方式:
#include <iostream>
#include <string>
#include <optional>
class User {
private:
std::string username_;
std::string password_;
std::string email_;
std::string phone_;
int age_;
// 构造器私有,只能通过Builder创建
User(std::string username, std::string password,
std::string email, std::string phone, int age)
: username_(std::move(username))
, password_(std::move(password))
, email_(std::move(email))
, phone_(std::move(phone))
, age_(age) {}
public:
class Builder {
private:
std::string username_;
std::string password_;
std::string email_;
std::string phone_;
int age_ = 0; // 默认值
public:
Builder& setUsername(const std::string& username) {
username_ = username;
return *this;
}
Builder& setPassword(const std::string& password) {
password_ = password;
return *this;
}
Builder& setEmail(const std::string& email) {
email_ = email;
return *this;
}
Builder& setPhone(const std::string& phone) {
phone_ = phone;
return *this;
}
Builder& setAge(int age) {
age_ = age;
return *this;
}
User build() {
// 可以在这里做参数校验
if (username_.empty()) {
throw std::runtime_error("username is required");
}
return User(username_, password_, email_, phone_, age_);
}
};
friend std::ostream& operator<<(std::ostream& os, const User& user) {
os << "User{username=" << user.username_
<< ", email=" << user.email_ << "}";
return os;
}
};
// 使用
int main() {
User user = User::Builder()
.setUsername("admin")
.setPassword("123456")
.setEmail("admin@example.com")
.setAge(25)
.build();
std::cout << user << std::endl;
return 0;
}
C++版本有几个要点:
- User的构造器设为private,只能通过Builder创建
- Builder的每个setter返回
Builder&,实现链式调用 - build()方法中可以做参数校验,确保对象状态合法
- 使用
std::move避免不必要的拷贝
我在一个C++后端项目中就用这种方式重构了配置类的创建。原来有十几个参数,每次new都让人头大。改成Builder后,代码可读性提升了一个档次。
知识体系结构图
下面这张图总结了建造者模式的核心知识点和它们之间的关系:
总结
建造者模式不是什么高深的技术,但它解决了一个很实际的问题——让对象创建变得清晰、可控。我个人建议:
- Java项目优先用Lombok的@Builder,省事
- C++项目手写Builder,注意用静态内部类和链式调用
- 参数超过4个时,果断上Builder
- 需要参数校验或默认值逻辑时,Builder比构造器灵活得多
嗯,今天就聊到这里。建造者模式看似简单,但用好了能让代码质量提升不少。下次你写一个参数很多的类时,不妨试试Builder模式。