深入实战建造者模式:在订单系统中的应用

本篇文章,我将深入讲解建造者模式在电商订单系统中的实际应用,展示如何通过这一模式优雅地构建复杂订单对象。

一、业务场景分析

电商系统中,订单创建是一个复杂的过程,涉及多个必填和可选参数:

  • ​必填项​​:订单ID、用户ID、商品列表、总金额

  • ​可选项​​:优惠信息、发票信息、收货地址、备注

  • ​约束条件​​:

    • 订单ID必须唯一
    • 商品列表不能为空
    • 总金额必须与商品金额总和匹配
    • 如果使用优惠券,必须验证有效性

传统的构造器或setter方法会导致:

  1. 构造器参数过多难以维护
  2. setter方法无法保证对象创建过程的原子性
  3. 参数验证逻辑分散

二、建造者模式实现

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();
    }
}

六、最佳实践建议

  1. ​将Builder作为静态内部类​:保持与目标类的紧密关联
  2. ​必填参数通过构造器传入​:保证必要的初始化
  3. ​返回this实现链式调用​:提升代码可读性
  4. ​build()方法执行验证​:确保对象有效性
  5. ​考虑对象不可变性​:构建完成后字段设为final
  6. ​防御性拷贝​:保护内部数据不被修改
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();
    }
}

通过建造者模式,我们实现了:

  1. 复杂订单对象的优雅构建
  2. 参数验证的集中管理
  3. 不可变对象的线程安全
  4. 代码可读性和可维护性的提升
相关推荐
一只鹿鹿鹿1 小时前
【网络安全】等级保护2.0解决方案
运维·安全·web安全·架构·信息化
杨DaB1 小时前
【SpringMVC】拦截器,实现小型登录验证
java·开发语言·后端·servlet·mvc
zandy10113 小时前
解构衡石嵌入式BI:统一语义层与API网关的原子化封装架构
架构
努力的小雨8 小时前
还在为调试提示词头疼?一个案例教你轻松上手!
后端
魔都吴所谓8 小时前
【go】语言的匿名变量如何定义与使用
开发语言·后端·golang
陈佬昔没带相机8 小时前
围观前后端对接的 TypeScript 最佳实践,我们缺什么?
前端·后端·api
你我约定有三8 小时前
分布式微服务--万字详解 微服务的各种负载均衡全场景以注意点
java·开发语言·windows·分布式·微服务·架构·负载均衡
Livingbody10 小时前
大模型微调数据集加载和分析
后端
Livingbody10 小时前
第一次免费使用A800显卡80GB显存微调Ernie大模型
后端
wydxry10 小时前
MOE架构详解:原理、应用与PyTorch实现
人工智能·pytorch·架构