建造者模式
基础概念
什么是建造者模式?
答案: 建造者模式(Builder Pattern)将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用场景:
- 对象有很多属性
- 对象创建过程复杂
- 需要生成不同表示的对象
优点:
- 封装性好,隐藏内部构建细节
- 扩展性好,易于扩展
- 便于控制构建过程
缺点:
- 产生多余的Builder对象
- 内部结构复杂时,建造者也会变复杂
实现方式
基本实现?
答案:
java
// 产品类
public class Computer {
private String cpu;
private String ram;
private String disk;
private String display;
private String keyboard;
// 私有构造器
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.disk = builder.disk;
this.display = builder.display;
this.keyboard = builder.keyboard;
}
// 静态内部类Builder
public static class Builder {
private String cpu;
private String ram;
private String disk;
private String display;
private String keyboard;
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder ram(String ram) {
this.ram = ram;
return this;
}
public Builder disk(String disk) {
this.disk = disk;
return this;
}
public Builder display(String display) {
this.display = display;
return this;
}
public Builder keyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Computer build() {
return new Computer(this);
}
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", disk='" + disk + '\'' +
", display='" + display + '\'' +
", keyboard='" + keyboard + '\'' +
'}';
}
}
// 使用
Computer computer = new Computer.Builder()
.cpu("Intel i7")
.ram("16GB")
.disk("512GB SSD")
.display("27寸")
.keyboard("机械键盘")
.build();传统建造者模式?
答案:
java
// 产品类
public class House {
private String foundation;
private String structure;
private String roof;
private String interior;
public void setFoundation(String foundation) {
this.foundation = foundation;
}
public void setStructure(String structure) {
this.structure = structure;
}
public void setRoof(String roof) {
this.roof = roof;
}
public void setInterior(String interior) {
this.interior = interior;
}
}
// 抽象建造者
public abstract class HouseBuilder {
protected House house = new House();
public abstract void buildFoundation();
public abstract void buildStructure();
public abstract void buildRoof();
public abstract void buildInterior();
public House getHouse() {
return house;
}
}
// 具体建造者:别墅
public class VillaBuilder extends HouseBuilder {
@Override
public void buildFoundation() {
house.setFoundation("别墅地基");
}
@Override
public void buildStructure() {
house.setStructure("别墅结构");
}
@Override
public void buildRoof() {
house.setRoof("别墅屋顶");
}
@Override
public void buildInterior() {
house.setInterior("别墅内饰");
}
}
// 具体建造者:公寓
public class ApartmentBuilder extends HouseBuilder {
@Override
public void buildFoundation() {
house.setFoundation("公寓地基");
}
@Override
public void buildStructure() {
house.setStructure("公寓结构");
}
@Override
public void buildRoof() {
house.setRoof("公寓屋顶");
}
@Override
public void buildInterior() {
house.setInterior("公寓内饰");
}
}
// 指挥者
public class Director {
private HouseBuilder builder;
public Director(HouseBuilder builder) {
this.builder = builder;
}
public House construct() {
builder.buildFoundation();
builder.buildStructure();
builder.buildRoof();
builder.buildInterior();
return builder.getHouse();
}
}
// 使用
HouseBuilder builder = new VillaBuilder();
Director director = new Director(builder);
House house = director.construct();实际应用
HTTP请求构建
答案:
java
public class HttpRequest {
private String url;
private String method;
private Map<String, String> headers;
private Map<String, String> params;
private String body;
private int timeout;
private HttpRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers;
this.params = builder.params;
this.body = builder.body;
this.timeout = builder.timeout;
}
public static class Builder {
private String url;
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private Map<String, String> params = new HashMap<>();
private String body;
private int timeout = 3000;
public Builder url(String url) {
this.url = url;
return this;
}
public Builder method(String method) {
this.method = method;
return this;
}
public Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}
public Builder param(String key, String value) {
this.params.put(key, value);
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder timeout(int timeout) {
this.timeout = timeout;
return this;
}
public HttpRequest build() {
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("URL不能为空");
}
return new HttpRequest(this);
}
}
public String execute() {
// 执行HTTP请求
return "Response";
}
}
// 使用
HttpRequest request = new HttpRequest.Builder()
.url("https://api.example.com/users")
.method("POST")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer token")
.body("{\"name\":\"张三\"}")
.timeout(5000)
.build();
String response = request.execute();SQL构建器
答案:
java
public class SqlBuilder {
private StringBuilder sql = new StringBuilder();
private String table;
private List<String> columns = new ArrayList<>();
private List<String> conditions = new ArrayList<>();
private String orderBy;
private Integer limit;
public SqlBuilder select(String... columns) {
this.columns.addAll(Arrays.asList(columns));
return this;
}
public SqlBuilder from(String table) {
this.table = table;
return this;
}
public SqlBuilder where(String condition) {
this.conditions.add(condition);
return this;
}
public SqlBuilder orderBy(String orderBy) {
this.orderBy = orderBy;
return this;
}
public SqlBuilder limit(int limit) {
this.limit = limit;
return this;
}
public String build() {
sql.append("SELECT ");
if (columns.isEmpty()) {
sql.append("*");
} else {
sql.append(String.join(", ", columns));
}
sql.append(" FROM ").append(table);
if (!conditions.isEmpty()) {
sql.append(" WHERE ").append(String.join(" AND ", conditions));
}
if (orderBy != null) {
sql.append(" ORDER BY ").append(orderBy);
}
if (limit != null) {
sql.append(" LIMIT ").append(limit);
}
return sql.toString();
}
}
// 使用
String sql = new SqlBuilder()
.select("id", "name", "age")
.from("user")
.where("age > 18")
.where("status = 1")
.orderBy("create_time DESC")
.limit(10)
.build();
System.out.println(sql);
// SELECT id, name, age FROM user WHERE age > 18 AND status = 1 ORDER BY create_time DESC LIMIT 10Lombok的@Builder
Lombok如何简化建造者模式?
答案:
java
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private String phone;
}
// 使用
User user = User.builder()
.id(1L)
.name("张三")
.age(20)
.email("zhangsan@example.com")
.phone("13800138000")
.build();Lombok生成的代码:
java
public class User {
private Long id;
private String name;
private Integer age;
private String email;
private String phone;
public static class UserBuilder {
private Long id;
private String name;
private Integer age;
private String email;
private String phone;
public UserBuilder id(Long id) {
this.id = id;
return this;
}
public UserBuilder name(String name) {
this.name = name;
return this;
}
// ... 其他方法
public User build() {
return new User(id, name, age, email, phone);
}
}
public static UserBuilder builder() {
return new UserBuilder();
}
}建造者模式 vs 工厂模式
两者的区别?
答案:
| 特性 | 建造者模式 | 工厂模式 |
|---|---|---|
| 关注点 | 如何创建对象 | 创建什么对象 |
| 对象复杂度 | 复杂对象 | 简单对象 |
| 构建过程 | 分步骤构建 | 一步创建 |
| 返回结果 | 同一类型不同表示 | 不同类型对象 |
使用场景:
- 建造者模式:对象有很多可选属性,需要灵活组合
- 工厂模式:根据类型创建不同的对象
实际框架中的应用
StringBuilder?
答案:
java
// StringBuilder就是建造者模式
StringBuilder sb = new StringBuilder();
sb.append("Hello")
.append(" ")
.append("World")
.append("!");
String result = sb.toString();MyBatis的SqlSessionFactoryBuilder?
答案:
java
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);Spring的BeanDefinitionBuilder?
答案:
java
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(User.class);
builder.addPropertyValue("name", "张三");
builder.addPropertyValue("age", 20);
BeanDefinition beanDefinition = builder.getBeanDefinition();练习题
- 建造者模式和工厂模式的区别?
- 什么时候使用建造者模式?
- 如何实现必填参数校验?
- Lombok的@Builder原理?
- 建造者模式如何支持不可变对象?