Skip to content

建造者模式

基础概念

什么是建造者模式?

答案: 建造者模式(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 10

Lombok的@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();

练习题

  1. 建造者模式和工厂模式的区别?
  2. 什么时候使用建造者模式?
  3. 如何实现必填参数校验?
  4. Lombok的@Builder原理?
  5. 建造者模式如何支持不可变对象?

Released under the MIT License.