news 2026/6/10 16:10:35

设计模式:构建者模式-示例二

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式:构建者模式-示例二

目录

一、场景分析:一个复杂的订单对象

二、代码实战:实现订单对象的建造者模式

1. 定义商品项和地址等辅助 DTO

2. 实现核心的 OrderDTO 及其建造者

3. 客户端如何使用

三、在芋道(Yudao)框架中的实践要点

四、总结


在无人售货柜这类复杂的业务系统中,一个 “订单” 对象通常包含大量信息,如商品明细、支付信息、收货地址、物流信息、用户信息等。如果使用传统的构造器或大量的setter方法来创建,代码会非常臃肿且容易出错。

下面,我们将结合现代 Java 开发的最佳实践(如链式调用、final字段保证不可变性)和芋道(Yudao)这类企业级开发框架的代码风格,为你展示如何为一个复杂的Order对象设计并实现建造者模式。

一、场景分析:一个复杂的订单对象

一个完整的订单OrderDTO(Data Transfer Object)可能包含以下部分:

  1. 基础信息:订单 ID、创建时间。
  2. 商品信息:商品列表(List<OrderItemDTO>)。
  3. 支付信息:支付方式、支付金额、交易流水号。
  4. 用户信息:下单用户 ID。
  5. 收货信息:收货地址、联系人、联系电话。
  6. 物流信息:物流单号、物流公司。

二、代码实战:实现订单对象的建造者模式

我们将采用静态内部类的方式来实现建造者,这是目前最流行和推荐的做法,因为它将建造者与产品紧密耦合在一起,封装性最好。

1. 定义商品项和地址等辅助 DTO

首先,我们定义订单中会用到的子对象。

java

运行

// OrderItemDTO.java import lombok.Data; import java.math.BigDecimal; @Data public class OrderItemDTO { private Long productId; private String productName; private Integer quantity; private BigDecimal unitPrice; } // AddressDTO.java import lombok.Data; @Data public class AddressDTO { private String receiver; private String phone; private String province; private String city; private String district; private String detailAddress; }
2. 实现核心的OrderDTO及其建造者

这是本次实战的核心。我们将建造者Builder作为OrderDTO的静态内部类,并使用链式调用。

java

运行

// OrderDTO.java import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * 订单数据传输对象 (DTO) * 采用建造者模式构建,保证对象创建的灵活性和可读性 */ public class OrderDTO { // === 基础信息 === private final Long id; private final LocalDateTime createTime; // === 商品信息 === private final List<OrderItemDTO> items; private final BigDecimal totalAmount; // === 支付信息 === private final String paymentMethod; private final String transactionId; // === 用户信息 === private final Long userId; // === 收货信息 === private final AddressDTO deliveryAddress; // === 物流信息 === private final String logisticsCode; private final String logisticsCompany; // 私有化构造器,只能通过Builder创建 private OrderDTO(Builder builder) { this.id = builder.id; this.createTime = builder.createTime; this.items = new ArrayList<>(builder.items); // 深拷贝,防止外部修改 this.totalAmount = calculateTotalAmount(builder.items); // 内部计算总金额 this.paymentMethod = builder.paymentMethod; this.transactionId = builder.transactionId; this.userId = builder.userId; this.deliveryAddress = builder.deliveryAddress; this.logisticsCode = builder.logisticsCode; this.logisticsCompany = builder.logisticsCompany; // 在这里可以添加校验逻辑,确保订单的完整性 validate(); } /** * 计算订单总金额 */ private BigDecimal calculateTotalAmount(List<OrderItemDTO> items) { return items.stream() .map(item -> item.getUnitPrice().multiply(BigDecimal.valueOf(item.getQuantity()))) .reduce(BigDecimal.ZERO, BigDecimal::add); } /** * 校验订单数据的完整性 */ private void validate() { Objects.requireNonNull(userId, "用户ID不能为空"); Objects.requireNonNull(paymentMethod, "支付方式不能为空"); Objects.requireNonNull(deliveryAddress, "收货地址不能为空"); if (items == null || items.isEmpty()) { throw new IllegalStateException("订单商品列表不能为空"); } if (totalAmount.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalStateException("订单总金额必须大于0"); } } // Getters for all fields // ... (此处省略所有字段的getter方法,实际项目中需要生成) @Override public String toString() { return "OrderDTO{" + "id=" + id + ", createTime=" + createTime + ", items=" + items + ", totalAmount=" + totalAmount + ", paymentMethod='" + paymentMethod + '\'' + ", transactionId='" + transactionId + '\'' + ", userId=" + userId + ", deliveryAddress=" + deliveryAddress + ", logisticsCode='" + logisticsCode + '\'' + ", logisticsCompany='" + logisticsCompany + '\'' + '}'; } /** * 静态内部建造者类 */ public static class Builder { // 1. 定义与OrderDTO完全相同的字段 private Long id; private LocalDateTime createTime; private final List<OrderItemDTO> items = new ArrayList<>(); private String paymentMethod; private String transactionId; private Long userId; private AddressDTO deliveryAddress; private String logisticsCode; private String logisticsCompany; // 2. 为每个字段提供链式的setter方法 public Builder id(Long id) { this.id = id; return this; } public Builder createTime(LocalDateTime createTime) { this.createTime = createTime; return this; } public Builder addItem(OrderItemDTO item) { this.items.add(item); return this; } public Builder items(List<OrderItemDTO> items) { this.items.addAll(items); return this; } public Builder paymentMethod(String paymentMethod) { this.paymentMethod = paymentMethod; return this; } public Builder transactionId(String transactionId) { this.transactionId = transactionId; return this; } public Builder userId(Long userId) { this.userId = userId; return this; } public Builder deliveryAddress(AddressDTO deliveryAddress) { this.deliveryAddress = deliveryAddress; return this; } public Builder logisticsCode(String logisticsCode) { this.logisticsCode = logisticsCode; return this; } public Builder logisticsCompany(String logisticsCompany) { this.logisticsCompany = logisticsCompany; return this; } // 3. 提供一个build方法,创建OrderDTO实例 public OrderDTO build() { // 可以在这里设置一些默认值 if (this.createTime == null) { this.createTime = LocalDateTime.now(); } return new OrderDTO(this); } } }
3. 客户端如何使用

客户端代码变得极其清晰和易于阅读,就像在 “描述” 一个订单的创建过程。

java

运行

// Client.java import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Arrays; public class Client { public static void main(String[] args) { // 1. 准备订单的各个组成部分 OrderItemDTO item1 = new OrderItemDTO(); item1.setProductId(1001L); item1.setProductName("可口可乐"); item1.setQuantity(2); item1.setUnitPrice(new BigDecimal("3.5")); OrderItemDTO item2 = new OrderItemDTO(); item2.setProductId(1002L); item2.setProductName("薯片"); item2.setQuantity(1); item2.setUnitPrice(new BigDecimal("5.0")); AddressDTO address = new AddressDTO(); address.setReceiver("张三"); address.setPhone("13800138000"); address.setProvince("广东省"); address.setCity("深圳市"); address.setDistrict("南山区"); address.setDetailAddress("科技园路100号"); // 2. 使用建造者模式创建订单 OrderDTO order = new OrderDTO.Builder() .id(9527L) .userId(101L) .paymentMethod("WECHAT_PAY") .transactionId("wx20231027...") .deliveryAddress(address) .addItem(item1) .addItem(item2) .logisticsCompany("顺丰速运") .logisticsCode("SF1234567890") // createTime 会在build时自动填充为当前时间 .build(); // 3. 打印订单信息 System.out.println("成功创建订单:"); System.out.println(order); } }

三、在芋道(Yudao)框架中的实践要点

芋道框架非常推崇领域驱动设计(DDD)和整洁的代码风格,上述建造者模式的实现方式与芋道的理念高度契合。在芋道项目中,你可以这样做:

  1. dto包中定义OrderDTO:将上述OrderDTO.java文件放在your-project-module-api/src/main/java/.../dto/目录下。

  2. 结合 Lombok 简化代码:虽然上面为了清晰展示原理而手动编写了构造器和getter,但在实际项目中,你可以使用 Lombok 的@Getter@Builder注解来极大地简化代码。

    java

    运行

    import lombok.Builder; import lombok.Getter; import lombok.Singular; // ... 其他 import @Getter @Builder(builderClassName = "Builder", toBuilder = true) public class OrderDTO { private final Long id; private final LocalDateTime createTime; @Singular // 用于集合,会自动生成 addItem 和 items 方法 private final List<OrderItemDTO> items; private final BigDecimal totalAmount; // 注意:Lombok的@Builder无法直接处理派生字段,需要手动处理 // ... 其他字段 // 为了处理totalAmount这种派生字段,可以使用一个私有构造器和静态工厂方法 // 或者在Service层计算好后再传入Builder }

    注意:Lombok 的@Builder对于需要在构建时动态计算的字段(如totalAmount)支持不佳。在这种情况下,手动实现建造者模式(如本示例所示)能提供更强大的控制力,因为你可以在OrderDTO的私有构造器中执行计算和校验逻辑。

  3. service层中使用:在你的OrderService实现类中,当需要创建一个OrderDTO返回给前端时,就可以使用这个建造者。

    java

    运行

    // OrderServiceImpl.java @Service public class OrderServiceImpl implements OrderService { @Override public OrderDTO getOrderDetails(Long orderId) { // ... 从数据库或领域模型中获取数据 ... OrderDO orderDO = orderMapper.selectById(orderId); List<OrderItemDO> itemDOs = orderItemMapper.selectListByOrderId(orderId); UserDO userDO = userMapper.selectById(orderDO.getUserId()); // ... 将DO转换为DTO的各个部分 ... List<OrderItemDTO> itemDTOs = itemDOs.stream().map(...).collect(Collectors.toList()); AddressDTO addressDTO = buildAddressDTO(userDO); // 使用建造者模式构建最终的OrderDTO return new OrderDTO.Builder() .id(orderDO.getId()) .createTime(orderDO.getCreateTime()) .userId(orderDO.getUserId()) .paymentMethod(orderDO.getPaymentMethod()) .transactionId(orderDO.getTransactionId()) .deliveryAddress(addressDTO) .items(itemDTOs) .logisticsCompany(orderDO.getLogisticsCompany()) .logisticsCode(orderDO.getLogisticsCode()) .build(); } }

四、总结

为订单对象实现建造者模式,带来了以下显著好处:

  • 可读性高:客户端代码如同自然语言,清晰地描述了订单的构成。
  • 灵活性强:可以轻松地增加或减少订单的组成部分,而无需修改构造函数。
  • 健壮性好:通过在build()OrderDTO构造器中进行校验,可以确保创建出的订单对象总是有效的。
  • 代码解耦:将复杂对象的创建逻辑封装在建造者中,客户端无需关心对象的内部结构和创建细节。

这种模式非常适合在无人售货柜这类业务复杂、对象属性繁多的系统中广泛应用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 11:08:03

Windows系统优化终极方案:Tron自动化清理工具完全指南

Windows系统优化终极方案&#xff1a;Tron自动化清理工具完全指南 【免费下载链接】tron Tron 项目地址: https://gitcode.com/gh_mirrors/tr/tron 在数字时代&#xff0c;Windows系统长期使用后难免出现运行缓慢、响应迟钝等问题。这些问题不仅影响工作效率&#xff0c…

作者头像 李华
网站建设 2026/6/10 14:32:34

Canvas动画库跨语言动画适配实战技巧与解决方案

Canvas动画库跨语言动画适配实战技巧与解决方案 【免费下载链接】Canvas Animate in Xcode without code 项目地址: https://gitcode.com/gh_mirrors/ca/Canvas 在全球化的iOS应用开发中&#xff0c;Canvas动画库的跨语言适配能力成为开发者必须掌握的关键技能。面对不同…

作者头像 李华
网站建设 2026/6/10 14:34:07

Invoify:零基础也能轻松创建专业发票的智能工具

Invoify&#xff1a;零基础也能轻松创建专业发票的智能工具 【免费下载链接】invoify An invoice generator app built using Next.js, Typescript, and Shadcn 项目地址: https://gitcode.com/GitHub_Trending/in/invoify 还在为繁琐的发票制作而烦恼吗&#xff1f;Inv…

作者头像 李华
网站建设 2026/6/10 15:49:30

PyTorch v2.7 + CUDA 最佳实践:使用官方镜像快速上手

PyTorch v2.7 CUDA 最佳实践&#xff1a;使用官方镜像快速上手 在深度学习项目中&#xff0c;最让人望而生畏的往往不是模型设计本身&#xff0c;而是环境搭建——尤其是当你要在多台 GPU 服务器上部署 PyTorch 并确保 CUDA 能稳定运行时。驱动版本不匹配、cuDNN 缺失、Python…

作者头像 李华
网站建设 2026/6/5 7:14:10

JDK1.8 32位Windows安装终极指南:快速搭建Java开发环境

JDK1.8 32位Windows安装终极指南&#xff1a;快速搭建Java开发环境 【免费下载链接】JDK1.832位Windows安装包 本仓库提供的是JDK1.8的最新版32位Windows安装包&#xff0c;文件名为jdk-8u271-windows-i586.exe。该安装包适用于32位的Windows操作系统&#xff0c;可以帮助开发者…

作者头像 李华