【设计模式手册006】建造者模式 - 复杂对象的优雅构建之道

设计模式手册006:建造者模式 - 复杂对象的优雅构建之道

本文是「设计模式手册」系列第006篇,我们将深入探讨建造者模式,这种模式将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

1. 场景:我们为何需要建造者模式?

在软件开发中,我们经常遇到复杂对象的创建问题,这些对象通常由多个部分构成,且构建步骤复杂。例如:

  • 电脑配置:由CPU、内存、硬盘、显卡等组成,配置灵活
  • 订单信息:包含订单项、收货地址、支付方式、优惠券等
  • 邮件消息:包含发件人、收件人、主题、正文、附件等
  • 查询条件:包含多个过滤条件、排序规则、分页参数等

传统做法的困境

java 复制代码
public class Computer {
    private String cpu;
    private String memory;
    private String hardDisk;
    private String graphicsCard;
    private String monitor;
    // 更多组件...

    // 方式1:巨型构造器
    public Computer(String cpu, String memory, String hardDisk, String graphicsCard, String monitor, ...) {
        this.cpu = cpu;
        this.memory = memory;
        this.hardDisk = hardDisk;
        this.graphicsCard = graphicsCard;
        this.monitor = monitor;
        // ... 更多赋值
    }

    // 方式2:setter方法,但对象可能处于不完整状态
    public Computer() {}

    public void setCpu(String cpu) { this.cpu = cpu; }
    public void setMemory(String memory) { this.memory = memory; }
    public void setHardDisk(String hardDisk) { this.hardDisk = hardDisk; }
    public void setGraphicsCard(String graphicsCard) { this.graphicsCard = graphicsCard; }
    public void setMonitor(String monitor) { this.monitor = monitor; }
    // ... 更多setter
}

// 使用方式1:构造器参数过多,难以阅读和维护
Computer computer = new Computer("Intel i7", "16GB", "1TB SSD", "NVIDIA GTX 3080", "27寸4K", ...);

// 使用方式2:setter方式,但可能忘记设置某些必要属性,导致对象不完整
Computer computer = new Computer();
computer.setCpu("Intel i7");
computer.setMemory("16GB");
// 忘记了设置硬盘,但程序不会立即报错,直到使用时报错

这种实现的痛点

  • 构造器参数过多,难以阅读和维护
  • setter方式可能导致对象状态不一致
  • 构建过程分散,无法保证构建的原子性
  • 无法保证必填参数的完整性

2. 建造者模式:定义与本质

2.1 模式定义

建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

2.2 核心角色

java 复制代码
// 产品类
public class Computer {
    private String cpu;
    private String memory;
    private String hardDisk;
    private String graphicsCard;
    private String monitor;
    // 更多组件...

    // 私有构造器,只能通过建造者创建
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.memory = builder.memory;
        this.hardDisk = builder.hardDisk;
        this.graphicsCard = builder.graphicsCard;
        this.monitor = builder.monitor;
    }

    // 静态内部类建造者
    public static class Builder {
        // 必选参数
        private String cpu;
        private String memory;
        private String hardDisk;

        // 可选参数
        private String graphicsCard;
        private String monitor;

        // 必选参数通过构造器传入
        public Builder(String cpu, String memory, String hardDisk) {
            this.cpu = cpu;
            this.memory = memory;
            this.hardDisk = hardDisk;
        }

        // 设置可选参数,返回建造者本身以支持链式调用
        public Builder setGraphicsCard(String graphicsCard) {
            this.graphicsCard = graphicsCard;
            return this;
        }

        public Builder setMonitor(String monitor) {
            this.monitor = monitor;
            return this;
        }

        // 构建方法
        public Computer build() {
            // 可以在此处进行校验
            if (cpu == null || memory == null || hardDisk == null) {
                throw new IllegalArgumentException("必选参数不能为空");
            }
            return new Computer(this);
        }
    }

    // getter方法
    public String getCpu() { return cpu; }
    public String getMemory() { return memory; }
    public String getHardDisk() { return hardDisk; }
    public String getGraphicsCard() { return graphicsCard; }
    public String getMonitor() { return monitor; }
}

3. 深入理解:建造者模式的多维视角

3.1 第一重:分离构建与表示

核心思想:将复杂对象的构建过程封装在建造者中,产品类只关注自身的表示。

java 复制代码
// 使用建造者模式
public class ComputerShop {
    public static void main(String[] args) {
        // 链式调用,清晰明了
        Computer gamingComputer = new Computer.Builder("Intel i9", "32GB", "2TB SSD")
                .setGraphicsCard("NVIDIA RTX 4090")
                .setMonitor("32寸4K 144Hz")
                .build();

        Computer officeComputer = new Computer.Builder("Intel i5", "16GB", "512GB SSD")
                .setMonitor("24寸1080P")
                .build();

        // 可以确保构建的对象是完整的
        System.out.println("游戏电脑配置: " + gamingComputer.getCpu() + ", " + gamingComputer.getGraphicsCard());
        System.out.println("办公电脑配置: " + officeComputer.getCpu() + ", " + officeComputer.getGraphicsCard());
    }
}

3.2 第二重:构建过程的控制

建造者可以控制构建步骤,确保构建过程的正确性。

java 复制代码
public class Order {
    private final String orderId;
    private final List<OrderItem> items;
    private final String shippingAddress;
    private final String paymentMethod;
    private final BigDecimal discount;

    private Order(OrderBuilder builder) {
        this.orderId = builder.orderId;
        this.items = builder.items;
        this.shippingAddress = builder.shippingAddress;
        this.paymentMethod = builder.paymentMethod;
        this.discount = builder.discount;
    }

    public static class OrderBuilder {
        private String orderId;
        private List<OrderItem> items;
        private String shippingAddress;
        private String paymentMethod;
        private BigDecimal discount;

        public OrderBuilder(String orderId) {
            this.orderId = orderId;
            this.items = new ArrayList<>();
        }

        public OrderBuilder addItem(OrderItem item) {
            this.items.add(item);
            return this;
        }

        public OrderBuilder setShippingAddress(String address) {
            this.shippingAddress = address;
            return this;
        }

        public OrderBuilder setPaymentMethod(String method) {
            this.paymentMethod = method;
            return this;
        }

        public OrderBuilder setDiscount(BigDecimal discount) {
            this.discount = discount;
            return this;
        }

        public Order build() {
            // 构建前的校验
            if (items.isEmpty()) {
                throw new IllegalStateException("订单必须包含至少一个商品");
            }
            if (shippingAddress == null) {
                throw new IllegalStateException("收货地址不能为空");
            }
            if (paymentMethod == null) {
                throw new IllegalStateException("支付方式不能为空");
            }
            return new Order(this);
        }
    }
}

3.3 第三重:与工厂模式的区别

  • 工厂模式:关注整体对象的创建,创建过程相对固定
  • 建造者模式:关注复杂对象的逐步构建,可以自定义构建过程

4. 实战案例:完整的订单构建系统

java 复制代码
// 订单项
public class OrderItem {
    private final String productId;
    private final String productName;
    private final int quantity;
    private final BigDecimal price;

    public OrderItem(String productId, String productName, int quantity, BigDecimal price) {
        this.productId = productId;
        this.productName = productName;
        this.quantity = quantity;
        this.price = price;
    }

    // getter方法
    public String getProductId() { return productId; }
    public String getProductName() { return productName; }
    public int getQuantity() { return quantity; }
    public BigDecimal getPrice() { return price; }
    public BigDecimal getTotalPrice() { return price.multiply(BigDecimal.valueOf(quantity)); }
}

// 订单类
public class Order {
    private final String orderId;
    private final List<OrderItem> items;
    private final String customerName;
    private final String shippingAddress;
    private final String paymentMethod;
    private final BigDecimal discount;
    private final LocalDateTime orderTime;

    private Order(OrderBuilder builder) {
        this.orderId = builder.orderId;
        this.items = Collections.unmodifiableList(builder.items);
        this.customerName = builder.customerName;
        this.shippingAddress = builder.shippingAddress;
        this.paymentMethod = builder.paymentMethod;
        this.discount = builder.discount;
        this.orderTime = builder.orderTime;
    }

    public static class OrderBuilder {
        private String orderId;
        private List<OrderItem> items;
        private String customerName;
        private String shippingAddress;
        private String paymentMethod;
        private BigDecimal discount;
        private LocalDateTime orderTime;

        public OrderBuilder(String orderId, String customerName) {
            this.orderId = orderId;
            this.customerName = customerName;
            this.items = new ArrayList<>();
            this.orderTime = LocalDateTime.now();
        }

        public OrderBuilder addItem(OrderItem item) {
            this.items.add(item);
            return this;
        }

        public OrderBuilder setShippingAddress(String shippingAddress) {
            this.shippingAddress = shippingAddress;
            return this;
        }

        public OrderBuilder setPaymentMethod(String paymentMethod) {
            this.paymentMethod = paymentMethod;
            return this;
        }

        public OrderBuilder setDiscount(BigDecimal discount) {
            this.discount = discount;
            return this;
        }

        public OrderBuilder setOrderTime(LocalDateTime orderTime) {
            this.orderTime = orderTime;
            return this;
        }

        public Order build() {
            // 构建前的校验
            if (items.isEmpty()) {
                throw new IllegalStateException("订单必须包含至少一个商品");
            }
            if (shippingAddress == null) {
                throw new IllegalStateException("收货地址不能为空");
            }
            if (paymentMethod == null) {
                throw new IllegalStateException("支付方式不能为空");
            }
            return new Order(this);
        }
    }

    // getter方法
    public String getOrderId() { return orderId; }
    public List<OrderItem> getItems() { return items; }
    public String getCustomerName() { return customerName; }
    public String getShippingAddress() { return shippingAddress; }
    public String getPaymentMethod() { return paymentMethod; }
    public BigDecimal getDiscount() { return discount; }
    public LocalDateTime getOrderTime() { return orderTime; }

    public BigDecimal getTotalAmount() {
        BigDecimal total = items.stream()
                .map(OrderItem::getTotalPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (discount != null) {
            total = total.subtract(discount);
        }
        return total;
    }
}

// 使用示例
public class OrderSystem {
    public static void main(String[] args) {
        Order order = new Order.OrderBuilder("ORDER001", "张三")
                .addItem(new OrderItem("P001", "iPhone 14", 1, new BigDecimal("5999")))
                .addItem(new OrderItem("P002", "AirPods", 1, new BigDecimal("1299")))
                .setShippingAddress("北京市海淀区xxx街道")
                .setPaymentMethod("支付宝")
                .setDiscount(new BigDecimal("100"))
                .build();

        System.out.println("订单ID: " + order.getOrderId());
        System.out.println("客户: " + order.getCustomerName());
        System.out.println("总金额: " + order.getTotalAmount());
        System.out.println("订单时间: " + order.getOrderTime());
    }
}

5. Lombok的@Builder注解

在实际开发中,我们可以使用Lombok来简化建造者模式的实现:

java 复制代码
import lombok.Builder;
import lombok.Value;

@Value
@Builder
public class Computer {
    private String cpu;
    private String memory;
    private String hardDisk;
    @Builder.Default private String graphicsCard = "集成显卡";
    private String monitor;
}

// 使用Lombok生成的建造者
public class LombokBuilderExample {
    public static void main(String[] args) {
        Computer computer = Computer.builder()
                .cpu("Intel i7")
                .memory("16GB")
                .hardDisk("1TB SSD")
                .graphicsCard("NVIDIA GTX 3080")
                .monitor("27寸4K")
                .build();

        System.out.println(computer.getCpu());
        System.out.println(computer.getGraphicsCard());
    }
}

6. 建造者模式的进阶用法

6.1 导演类(Director)

当构建过程非常复杂且固定时,可以引入导演类来封装构建过程:

java 复制代码
// 导演类
public class ComputerDirector {
    private Computer.Builder builder;

    public ComputerDirector(Computer.Builder builder) {
        this.builder = builder;
    }

    public Computer constructGamingComputer() {
        return builder.setGraphicsCard("NVIDIA RTX 4090")
                     .setMonitor("32寸4K 144Hz")
                     .build();
    }

    public Computer constructOfficeComputer() {
        return builder.setGraphicsCard("集成显卡")
                     .setMonitor("24寸1080P")
                     .build();
    }

    public Computer constructDevelopmentComputer() {
        return builder.setGraphicsCard("NVIDIA RTX 3080")
                     .setMonitor("27寸4K")
                     .build();
    }
}

// 使用导演类
public class DirectorExample {
    public static void main(String[] args) {
        Computer.Builder builder = new Computer.Builder("Intel i7", "16GB", "1TB SSD");
        ComputerDirector director = new ComputerDirector(builder);

        Computer gamingComputer = director.constructGamingComputer();
        Computer officeComputer = director.constructOfficeComputer();

        System.out.println("游戏电脑: " + gamingComputer.getGraphicsCard());
        System.out.println("办公电脑: " + officeComputer.getGraphicsCard());
    }
}

6.2 泛型建造者

java 复制代码
// 泛型建造者接口
public interface Builder<T> {
    T build();
}

// 具体建造者实现
public class ComputerBuilder implements Builder<Computer> {
    private String cpu;
    private String memory;
    private String hardDisk;
    private String graphicsCard;
    private String monitor;

    public ComputerBuilder setCpu(String cpu) {
        this.cpu = cpu;
        return this;
    }

    public ComputerBuilder setMemory(String memory) {
        this.memory = memory;
        return this;
    }

    public ComputerBuilder setHardDisk(String hardDisk) {
        this.hardDisk = hardDisk;
        return this;
    }

    public ComputerBuilder setGraphicsCard(String graphicsCard) {
        this.graphicsCard = graphicsCard;
        return this;
    }

    public ComputerBuilder setMonitor(String monitor) {
        this.monitor = monitor;
        return this;
    }

    @Override
    public Computer build() {
        // 校验和构建逻辑
        return new Computer(cpu, memory, hardDisk, graphicsCard, monitor);
    }
}

7. 建造者模式 vs 其他模式

7.1 建造者模式 vs 工厂模式

  • 工厂模式:关注整体对象的创建,创建过程相对固定
  • 建造者模式:关注复杂对象的逐步构建,可以自定义构建过程

7.2 建造者模式 vs 原型模式

  • 建造者模式:通过一步步构建来创建新对象
  • 原型模式:通过复制现有对象来创建新对象

7.3 建造者模式 vs 策略模式

  • 建造者模式:关注对象的构建过程
  • 策略模式:关注算法的选择和替换

8. 总结与思考

8.1 建造者模式的优点

  1. 封装性好:构建过程与产品表示分离
  2. 扩展性好:不同的建造者可以创建不同的产品
  3. 控制构建过程:可以精细控制构建步骤
  4. 避免重叠构造器:解决多个构造参数的问题
  5. 保证产品完整性:可以在build方法中校验

8.2 建造者模式的缺点

  1. 代码重复:产品类和建造者类的代码相似
  2. 增加复杂性:需要创建多个类
  3. 性能影响:对象创建需要多步,可能影响性能

8.3 设计思考

建造者模式的本质是**"分步骤构建复杂对象"**。它将复杂对象的构建过程分解为多个简单的步骤,使得构建过程更加清晰和可控。

深入思考的角度

"建造者模式通过将构建过程分解为多个步骤,使得我们可以更加精细地控制对象的创建。它不仅解决了'重叠构造器'的问题,还提供了更好的可读性和灵活性。"

在实际应用中,建造者模式有很多优秀的实践:

  • Java中的StringBuilder和StringBuffer
  • Spring中的BeanDefinitionBuilder
  • Jackson中的JsonNodeBuilder
  • 各种查询条件的构建

从系统设计的角度看,建造者模式特别适合构建那些包含多个组成部分的复杂对象,尤其是当这些组成部分的构建顺序或组合方式可能变化时。

使用场景判断

  • 适合:对象有多个组成部分,构建过程复杂,需要不同的构建顺序或组合
  • 不适合:对象构建简单,或者构建过程固定不变

下一篇预告:设计模式手册007 - 原型模式:如何通过复制现有对象来创建新对象?


版权声明:本文为CSDN博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

相关推荐
S***q1922 小时前
后端服务架构设计:从单体到微服务
java·微服务·架构
T***u3332 小时前
微服务书籍
java·微服务·架构
ZHE|张恒2 小时前
设计模式(二)工厂方法模式 — 把创建权限下放给子类,像“可扩展的生产线”
java·开发语言·设计模式
qq_12498707533 小时前
基于springboot的兴趣生活展示交流平台的设计与实现(源码+论文+部署+安装)
java·spring boot·生活·毕设
明洞日记3 小时前
【设计模式手册008】适配器模式 - 让不兼容的接口协同工作
java·设计模式·适配器模式
zzz海羊3 小时前
VSCode配置java中的lombok
java·开发语言·vscode
A-code3 小时前
Git 多模块项目管理
java·开发语言
TDengine (老段)3 小时前
TDengine 字符串函数 Replace 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
java_logo3 小时前
BUSYBOX Docker 容器化部署指南
java·运维·python·nginx·docker·容器·运维开发