本篇文章,我将深入讲解建造者模式在电商订单系统中的实际应用,展示如何通过这一模式优雅地构建复杂订单对象。
一、业务场景分析
电商系统中,订单创建是一个复杂的过程,涉及多个必填和可选参数:
- 
必填项:订单ID、用户ID、商品列表、总金额 
- 
可选项:优惠信息、发票信息、收货地址、备注 
- 
约束条件: - 订单ID必须唯一
- 商品列表不能为空
- 总金额必须与商品金额总和匹配
- 如果使用优惠券,必须验证有效性
 
传统的构造器或setter方法会导致:
- 构造器参数过多难以维护
- setter方法无法保证对象创建过程的原子性
- 参数验证逻辑分散
二、建造者模式实现
1. 订单实体类设计
            
            
              java
              
              
            
          
          public class Order {
    // 必填字段
    private final String orderId;
    private final Long userId;
    private final List<OrderItem> items;
    private final BigDecimal totalAmount;
    
    // 可选字段
    private final CouponInfo coupon;
    private final InvoiceInfo invoice;
    private final ShippingAddress address;
    private final String remark;
    
    // 私有构造器,只能通过Builder创建
    private Order(Builder builder) {
        this.orderId = builder.orderId;
        this.userId = builder.userId;
        this.items = Collections.unmodifiableList(builder.items);
        this.totalAmount = builder.totalAmount;
        this.coupon = builder.coupon;
        this.invoice = builder.invoice;
        this.address = builder.address;
        this.remark = builder.remark;
    }
    
    // getter方法...
    
    public static Builder builder(String orderId, Long userId) {
        return new Builder(orderId, userId);
    }
    
    // 建造者静态内部类
    public static class Builder {
        // 必填参数
        private final String orderId;
        private final Long userId;
        
        // 可选参数
        private List<OrderItem> items = new ArrayList<>();
        private BigDecimal totalAmount = BigDecimal.ZERO;
        private CouponInfo coupon;
        private InvoiceInfo invoice;
        private ShippingAddress address;
        private String remark;
        
        public Builder(String orderId, Long userId) {
            this.orderId = Objects.requireNonNull(orderId);
            this.userId = Objects.requireNonNull(userId);
        }
        
        public Builder items(List<OrderItem> items) {
            this.items = new ArrayList<>(items); // 防御性拷贝
            return this;
        }
        
        public Builder addItem(OrderItem item) {
            this.items.add(Objects.requireNonNull(item));
            return this;
        }
        
        public Builder totalAmount(BigDecimal amount) {
            this.totalAmount = Objects.requireNonNull(amount);
            return this;
        }
        
        public Builder coupon(CouponInfo coupon) {
            this.coupon = coupon;
            return this;
        }
        
        public Builder invoice(InvoiceInfo invoice) {
            this.invoice = invoice;
            return this;
        }
        
        public Builder address(ShippingAddress address) {
            this.address = address;
            return this;
        }
        
        public Builder remark(String remark) {
            this.remark = remark;
            return this;
        }
        
        public Order build() {
            validate();
            calculateTotal(); // 自动计算总金额
            return new Order(this);
        }
        
        private void validate() {
            if (orderId.isEmpty()) {
                throw new IllegalArgumentException("订单ID不能为空");
            }
            if (items.isEmpty()) {
                throw new IllegalArgumentException("订单必须包含至少一件商品");
            }
            if (coupon != null && !coupon.isValid()) {
                throw new IllegalArgumentException("优惠券已失效");
            }
        }
        
        private void calculateTotal() {
            if (totalAmount.compareTo(BigDecimal.ZERO) == 0) {
                // 未设置总金额时自动计算
                totalAmount = items.stream()
                    .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
                
                // 应用优惠
                if (coupon != null) {
                    totalAmount = coupon.applyDiscount(totalAmount);
                }
            }
        }
    }
}2. 订单项实体类
            
            
              java
              
              
            
          
          public class OrderItem {
    private final String sku;
    private final String name;
    private final BigDecimal price;
    private final int quantity;
    
    public OrderItem(String sku, String name, BigDecimal price, int quantity) {
        this.sku = Objects.requireNonNull(sku);
        this.name = Objects.requireNonNull(name);
        this.price = Objects.requireNonNull(price);
        if (quantity <= 0) {
            throw new IllegalArgumentException("数量必须大于0");
        }
        this.quantity = quantity;
    }
    
    // getter方法...
}3. 订单服务中使用
            
            
              java
              
              
            
          
          @Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private CouponService couponService;
    
    @Transactional
    public String createOrder(OrderRequest request) {
        // 验证库存
        request.getItems().forEach(item -> 
            inventoryService.checkStock(item.getSku(), item.getQuantity())
        );
        
        // 构建订单
        Order.Builder builder = Order.builder(
            generateOrderId(), 
            request.getUserId()
        );
        
        // 添加商品项
        request.getItems().forEach(item ->
            builder.addItem(new OrderItem(
                item.getSku(),
                item.getName(),
                item.getPrice(),
                item.getQuantity()
            ))
        );
        
        // 应用优惠券
        if (request.getCouponCode() != null) {
            CouponInfo coupon = couponService.validateCoupon(
                request.getUserId(), 
                request.getCouponCode()
            );
            builder.coupon(coupon);
        }
        
        // 设置其他信息
        builder.address(request.getAddress())
              .remark(request.getRemark());
        
        // 构建订单
        Order order = builder.build();
        
        // 保存订单
        return orderRepository.save(order).getId();
    }
}三、建造者模式的优势
参数设置灵活:可选参数可以自由组合,不必记忆参数顺序
            
            
              java
              
              
            
          
          // 多种构建方式
Order order1 = Order.builder("123", 456L)
    .addItem(item1)
    .addItem(item2)
    .build();
    
Order order2 = Order.builder("789", 101L)
    .items(itemList)
    .coupon(coupon)
    .address(address)
    .build();对象不可变:构建完成后对象状态不可修改,线程安全
            
            
              java
              
              
            
          
          // 编译错误,无法修改
order.setTotalAmount(newAmount); 构建过程可控:可以在build()方法中添加验证逻辑
            
            
              java
              
              
            
          
          public Order build() {
    validate();
    calculateTotal();
    return new Order(this);
}代码可读性强:链式调用使代码更清晰
            
            
              scss
              
              
            
          
          Order.builder(id, userId)
    .addItem(item1)
    .addItem(item2)
    .coupon(coupon)
    .build();参数隔离:建造者实例是独立的,可以复用
            
            
              java
              
              
            
          
          Order.Builder builder = Order.builder("123", 456L)
    .address(address);
    
Order order1 = builder.addItem(item1).build();
Order order2 = builder.addItem(item2).build();四、与其他创建型模式对比
| 模式 | 适用场景 | 优点 | 缺点 | 
|---|---|---|---|
| 建造者模式 | 创建复杂对象,特别是可选参数多的情况 | 参数灵活,对象不可变 | 需要额外创建Builder类 | 
| 工厂方法 | 创建单一类型对象 | 解耦具体类 | 不适合复杂对象创建 | 
| 抽象工厂 | 创建产品族 | 支持产品族扩展 | 难以支持新种类产品 | 
五、实际业务扩展
1. 订单折扣策略
            
            
              java
              
              
            
          
          public interface DiscountStrategy {
    BigDecimal applyDiscount(BigDecimal amount);
}
public class CouponDiscount implements DiscountStrategy {
    private final BigDecimal discountAmount;
    
    public CouponDiscount(BigDecimal discountAmount) {
        this.discountAmount = discountAmount;
    }
    
    @Override
    public BigDecimal applyDiscount(BigDecimal amount) {
        return amount.subtract(discountAmount).max(BigDecimal.ZERO);
    }
}
// 在Builder中使用
public Builder discountStrategy(DiscountStrategy strategy) {
    this.discountStrategy = strategy;
    return this;
}
private void calculateTotal() {
    totalAmount = items.stream()
        .map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
        .reduce(BigDecimal.ZERO, BigDecimal::add);
    
    if (discountStrategy != null) {
        totalAmount = discountStrategy.applyDiscount(totalAmount);
    }
}2. 订单历史版本
            
            
              java
              
              
            
          
          public class OrderHistory {
    private final Order order;
    private final Instant createdAt;
    
    public OrderHistory(Order order) {
        this.order = order;
        this.createdAt = Instant.now();
    }
    
    // 恢复订单到该版本
    public Order restore() {
        return Order.builder(order.getOrderId(), order.getUserId())
            .items(new ArrayList<>(order.getItems()))
            .totalAmount(order.getTotalAmount())
            // 设置其他字段...
            .build();
    }
}六、最佳实践建议
- 将Builder作为静态内部类:保持与目标类的紧密关联
- 必填参数通过构造器传入:保证必要的初始化
- 返回this实现链式调用:提升代码可读性
- build()方法执行验证:确保对象有效性
- 考虑对象不可变性:构建完成后字段设为final
- 防御性拷贝:保护内部数据不被修改
            
            
              java
              
              
            
          
          public Builder items(List<OrderItem> items) {
    this.items = new ArrayList<>(items); // 防御性拷贝
    return this;
}七、完整订单服务示例
            
            
              java
              
              
            
          
          @Service
@Transactional
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderRepository orderRepo;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private CouponService couponService;
    @Autowired
    private NotificationService notificationService;
    
    @Override
    public String placeOrder(OrderRequest request) {
        // 1. 验证库存
        validateInventory(request.getItems());
        
        // 2. 验证优惠券
        CouponInfo coupon = validateCoupon(request);
        
        // 3. 构建订单
        Order order = buildOrder(request, coupon);
        
        // 4. 保存订单
        orderRepo.save(order);
        
        // 5. 扣减库存
        inventoryService.deductStock(request.getItems());
        
        // 6. 发送通知
        notificationService.sendOrderCreatedNotification(order);
        
        return order.getOrderId();
    }
    
    private void validateInventory(List<OrderItemRequest> items) {
        items.forEach(item ->
            inventoryService.checkStock(item.getSku(), item.getQuantity())
        );
    }
    
    private CouponInfo validateCoupon(OrderRequest request) {
        if (request.getCouponCode() == null) {
            return null;
        }
        return couponService.validateCoupon(
            request.getUserId(), 
            request.getCouponCode()
        );
    }
    
    private Order buildOrder(OrderRequest request, CouponInfo coupon) {
        Order.Builder builder = Order.builder(
            generateOrderId(),
            request.getUserId()
        );
        
        // 添加商品项
        request.getItems().forEach(item ->
            builder.addItem(convertToOrderItem(item))
        );
        
        // 设置可选信息
        if (coupon != null) {
            builder.coupon(coupon);
        }
        if (request.getAddress() != null) {
            builder.address(request.getAddress());
        }
        if (request.getRemark() != null) {
            builder.remark(request.getRemark());
        }
        
        return builder.build();
    }
    
    private OrderItem convertToOrderItem(OrderItemRequest request) {
        return new OrderItem(
            request.getSku(),
            request.getName(),
            request.getPrice(),
            request.getQuantity()
        );
    }
    
    private String generateOrderId() {
        return "ORD-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
    }
}通过建造者模式,我们实现了:
- 复杂订单对象的优雅构建
- 参数验证的集中管理
- 不可变对象的线程安全
- 代码可读性和可维护性的提升