Java记录类与密封类实战:构建不可变领域模型的最佳实践

2025-10-24 0 768

引言

Java 14引入的记录类(Records)和密封类(Sealed Classes)代表了Java语言向更简洁、更安全编程模式的重要演进。这些特性特别适合构建不可变的领域模型,能够显著减少样板代码并增强编译时类型安全。本文将深入探讨如何结合使用记录类和密封类来构建健壮的领域驱动设计架构。

一、记录类(Records)深度解析

1.1 记录类基础语法与特性

记录类提供了一种简洁的方式来声明不可变数据载体,自动生成构造函数、访问器、equals、hashCode和toString方法。

// 基础记录类定义
public record Customer(
    String customerId,
    String name,
    Email email,
    LocalDate registrationDate
) {
    // 紧凑构造函数用于验证
    public Customer {
        if (customerId == null || customerId.isBlank()) {
            throw new IllegalArgumentException("客户ID不能为空");
        }
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("客户名称不能为空");
        }
        Objects.requireNonNull(email, "邮箱不能为空");
        Objects.requireNonNull(registrationDate, "注册日期不能为空");
    }
    
    // 自定义方法
    public boolean isPremiumCustomer() {
        return registrationDate.isBefore(LocalDate.now().minusYears(1));
    }
}

// 值对象记录类
public record Email(String value) {
    private static final Pattern EMAIL_PATTERN = 
        Pattern.compile("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$");
    
    public Email {
        if (value == null || !EMAIL_PATTERN.matcher(value).matches()) {
            throw new IllegalArgumentException("无效的邮箱格式: " + value);
        }
    }
    
    public String domain() {
        return value.substring(value.indexOf('@') + 1);
    }
}

1.2 记录类与模式匹配的结合

public class RecordPatternMatching {
    
    // 使用instanceof模式匹配
    public static void processOrder(Object order) {
        if (order instanceof OrderRecord(String orderId, BigDecimal amount, OrderStatus status)) {
            System.out.printf("处理订单 %s, 金额: %.2f, 状态: %s%n", 
                orderId, amount, status);
        }
    }
    
    // switch表达式中的记录模式
    public static String getOrderDescription(OrderRecord order) {
        return switch(order) {
            case OrderRecord(String id, BigDecimal amount, OrderStatus status) 
                when amount.compareTo(new BigDecimal("1000")) > 0 -> 
                "大额订单 " + id;
            case OrderRecord(String id, _, OrderStatus.PENDING) -> 
                "待处理订单 " + id;
            case OrderRecord(String id, _, OrderStatus.COMPLETED) -> 
                "已完成订单 " + id;
            default -> "普通订单 " + order.orderId();
        };
    }
}

二、密封类(Sealed Classes)高级应用

2.1 密封类基础定义

// 定义支付方式的密封层次结构
public sealed interface PaymentMethod 
    permits CreditCardPayment, BankTransfer, DigitalWallet {
    
    String getPaymentId();
    BigDecimal getAmount();
    PaymentStatus processPayment();
}

// 信用卡支付实现
public final record CreditCardPayment(
    String paymentId,
    BigDecimal amount,
    String cardNumber,
    String expiryDate,
    String cvv
) implements PaymentMethod {
    
    public CreditCardPayment {
        validateCardNumber(cardNumber);
        validateExpiryDate(expiryDate);
    }
    
    @Override
    public PaymentStatus processPayment() {
        // 信用卡处理逻辑
        System.out.println("处理信用卡支付: " + cardNumber);
        return PaymentStatus.SUCCESS;
    }
    
    private void validateCardNumber(String cardNumber) {
        if (cardNumber == null || !cardNumber.matches("\d{16}")) {
            throw new IllegalArgumentException("无效的信用卡号");
        }
    }
    
    private void validateExpiryDate(String expiryDate) {
        // 有效期验证逻辑
    }
}

// 银行转账实现
public final record BankTransfer(
    String paymentId,
    BigDecimal amount,
    String accountNumber,
    String bankCode,
    String recipientName
) implements PaymentMethod {
    
    @Override
    public PaymentStatus processPayment() {
        // 银行转账逻辑
        System.out.println("处理银行转账到: " + recipientName);
        return PaymentStatus.PENDING;
    }
}

// 数字钱包实现
public final record DigitalWallet(
    String paymentId,
    BigDecimal amount,
    WalletType walletType,
    String walletId
) implements PaymentMethod {
    
    @Override
    public PaymentStatus processPayment() {
        // 数字钱包处理逻辑
        System.out.println("处理" + walletType + "钱包支付");
        return PaymentStatus.SUCCESS;
    }
}

2.2 密封类与模式匹配的完美结合

public class PaymentProcessor {
    
    public static PaymentResult process(PaymentMethod payment) {
        return switch(payment) {
            case CreditCardPayment(String id, BigDecimal amount, String cardNumber, _, _) -> {
                System.out.println("处理信用卡支付,卡号: " + maskCardNumber(cardNumber));
                yield new PaymentResult(id, PaymentStatus.SUCCESS, "信用卡支付成功");
            }
            case BankTransfer(String id, BigDecimal amount, String account, String bank, String recipient) -> {
                System.out.println("处理银行转账到: " + recipient);
                yield new PaymentResult(id, PaymentStatus.PENDING, "银行转账处理中");
            }
            case DigitalWallet(String id, BigDecimal amount, WalletType type, String walletId) -> {
                System.out.println("处理" + type.getDisplayName() + "支付");
                yield new PaymentResult(id, PaymentStatus.SUCCESS, type.getDisplayName() + "支付成功");
            }
            // 不需要default分支,因为密封类已经覆盖所有情况
        };
    }
    
    private static String maskCardNumber(String cardNumber) {
        return "****-****-****-" + cardNumber.substring(12);
    }
}

三、电商订单系统领域模型实战

3.1 核心领域模型定义

// 订单状态密封类
public sealed interface OrderStatus 
    permits OrderStatus.Pending, OrderStatus.Confirmed, 
            OrderStatus.Shipped, OrderStatus.Delivered, OrderStatus.Cancelled {
    
    record Pending() implements OrderStatus {}
    record Confirmed() implements OrderStatus {}
    record Shipped(LocalDate shippedDate, String trackingNumber) implements OrderStatus {}
    record Delivered(LocalDate deliveredDate, String recipient) implements OrderStatus {}
    record Cancelled(LocalDate cancelledDate, String reason) implements OrderStatus {}
}

// 订单项记录类
public record OrderItem(
    ProductId productId,
    String productName,
    BigDecimal unitPrice,
    int quantity,
    BigDecimal discount
) {
    public OrderItem {
        if (quantity <= 0) {
            throw new IllegalArgumentException("数量必须大于0");
        }
        if (unitPrice.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("单价不能为负数");
        }
    }
    
    public BigDecimal calculateLineTotal() {
        return unitPrice.multiply(BigDecimal.valueOf(quantity))
                       .subtract(discount);
    }
}

// 主订单记录类
public record Order(
    OrderId orderId,
    CustomerId customerId,
    List items,
    OrderStatus status,
    LocalDateTime createdAt,
    Address shippingAddress,
    Address billingAddress
) {
    public Order {
        Objects.requireNonNull(orderId, "订单ID不能为空");
        Objects.requireNonNull(customerId, "客户ID不能为空");
        Objects.requireNonNull(items, "订单项不能为空");
        Objects.requireNonNull(status, "订单状态不能为空");
        Objects.requireNonNull(createdAt, "创建时间不能为空");
        
        if (items.isEmpty()) {
            throw new IllegalArgumentException("订单必须包含至少一个商品");
        }
    }
    
    public BigDecimal calculateTotal() {
        return items.stream()
            .map(OrderItem::calculateLineTotal)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    
    public boolean canBeCancelled() {
        return switch(status) {
            case OrderStatus.Pending pending -> true;
            case OrderStatus.Confirmed confirmed -> true;
            case OrderStatus.Shipped shipped -> false;
            case OrderStatus.Delivered delivered -> false;
            case OrderStatus.Cancelled cancelled -> false;
        };
    }
}

3.2 订单服务实现

public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;
    private final InventoryService inventoryService;
    
    public OrderService(OrderRepository orderRepository, 
                       PaymentService paymentService,
                       InventoryService inventoryService) {
        this.orderRepository = orderRepository;
        this.paymentService = paymentService;
        this.inventoryService = inventoryService;
    }
    
    public OrderResult createOrder(CreateOrderCommand command) {
        // 验证库存
        var inventoryResults = command.items().stream()
            .map(item -> inventoryService.checkAvailability(item.productId(), item.quantity()))
            .toList();
        
        if (inventoryResults.stream().anyMatch(result -> !result.available())) {
            var unavailableItems = inventoryResults.stream()
                .filter(result -> !result.available())
                .map(InventoryResult::productId)
                .toList();
            return OrderResult.failure("库存不足: " + unavailableItems);
        }
        
        // 创建订单
        var order = new Order(
            OrderId.generate(),
            command.customerId(),
            command.items(),
            new OrderStatus.Pending(),
            LocalDateTime.now(),
            command.shippingAddress(),
            command.billingAddress()
        );
        
        var savedOrder = orderRepository.save(order);
        return OrderResult.success(savedOrder);
    }
    
    public OrderResult processOrderPayment(OrderId orderId, PaymentMethod paymentMethod) {
        var order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
        
        if (!(order.status() instanceof OrderStatus.Pending)) {
            return OrderResult.failure("订单状态不允许支付");
        }
        
        var paymentResult = paymentService.processPayment(paymentMethod);
        
        var updatedOrder = switch(paymentResult.status()) {
            case SUCCESS -> orderRepository.updateStatus(orderId, new OrderStatus.Confirmed());
            case PENDING -> orderRepository.updateStatus(orderId, new OrderStatus.Confirmed());
            case FAILED -> order;
        };
        
        return OrderResult.success(updatedOrder);
    }
    
    public record CreateOrderCommand(
        CustomerId customerId,
        List items,
        Address shippingAddress,
        Address billingAddress
    ) {}
    
    public record OrderResult(Order order, boolean success, String message) {
        public static OrderResult success(Order order) {
            return new OrderResult(order, true, "操作成功");
        }
        
        public static OrderResult failure(String message) {
            return new OrderResult(null, false, message);
        }
    }
}

3.3 领域事件系统

// 领域事件密封类
public sealed interface DomainEvent 
    permits OrderCreatedEvent, OrderConfirmedEvent, OrderShippedEvent, 
            OrderCancelledEvent, PaymentProcessedEvent {
    
    String eventId();
    LocalDateTime occurredOn();
}

public record OrderCreatedEvent(
    String eventId,
    OrderId orderId,
    CustomerId customerId,
    BigDecimal totalAmount,
    LocalDateTime occurredOn
) implements DomainEvent {
    public OrderCreatedEvent(Order order) {
        this(
            UUID.randomUUID().toString(),
            order.orderId(),
            order.customerId(),
            order.calculateTotal(),
            LocalDateTime.now()
        );
    }
}

public record OrderConfirmedEvent(
    String eventId,
    OrderId orderId,
    LocalDateTime confirmedAt,
    LocalDateTime occurredOn
) implements DomainEvent {
    public OrderConfirmedEvent(OrderId orderId) {
        this(
            UUID.randomUUID().toString(),
            orderId,
            LocalDateTime.now(),
            LocalDateTime.now()
        );
    }
}

// 事件处理器
public class DomainEventProcessor {
    
    public void processEvent(DomainEvent event) {
        switch(event) {
            case OrderCreatedEvent e -> handleOrderCreated(e);
            case OrderConfirmedEvent e -> handleOrderConfirmed(e);
            case OrderShippedEvent e -> handleOrderShipped(e);
            case OrderCancelledEvent e -> handleOrderCancelled(e);
            case PaymentProcessedEvent e -> handlePaymentProcessed(e);
        }
    }
    
    private void handleOrderCreated(OrderCreatedEvent event) {
        // 发送通知、更新报表等
        System.out.println("处理订单创建事件: " + event.orderId());
    }
    
    private void handleOrderConfirmed(OrderConfirmedEvent event) {
        // 库存扣减、物流准备等
        System.out.println("处理订单确认事件: " + event.orderId());
    }
}

四、最佳实践与性能考量

4.1 记录类的序列化优化

public class RecordSerialization {
    
    // JSON序列化示例
    public String serializeOrderToJson(Order order) {
        return switch(order.status()) {
            case OrderStatus.Pending pending -> 
                String.format("""
                {
                    "orderId": "%s",
                    "status": "pending",
                    "total": %.2f
                }
                """, order.orderId().value(), order.calculateTotal());
                
            case OrderStatus.Shipped shipped -> 
                String.format("""
                {
                    "orderId": "%s",
                    "status": "shipped",
                    "trackingNumber": "%s",
                    "shippedDate": "%s"
                }
                """, order.orderId().value(), shipped.trackingNumber(), shipped.shippedDate());
                
            case OrderStatus.Delivered delivered -> 
                String.format("""
                {
                    "orderId": "%s",
                    "status": "delivered",
                    "deliveredDate": "%s",
                    "recipient": "%s"
                }
                """, order.orderId().value(), delivered.deliveredDate(), delivered.recipient());
                
            default -> 
                String.format("""
                {
                    "orderId": "%s",
                    "status": "%s"
                }
                """, order.orderId().value(), order.status().getClass().getSimpleName().toLowerCase());
        };
    }
}

4.2 内存占用与性能分析

public class PerformanceAnalyzer {
    
    public static void analyzeMemoryUsage() {
        // 记录类相比传统类的内存优势
        var traditionalOrder = new TraditionalOrder("123", "customer1", 
            List.of(new TraditionalOrderItem("prod1", 2, new BigDecimal("99.99"))));
        
        var recordOrder = new Order(
            new OrderId("123"),
            new CustomerId("customer1"),
            List.of(new OrderItem(new ProductId("prod1"), "商品1", 
                    new BigDecimal("99.99"), 2, BigDecimal.ZERO)),
            new OrderStatus.Pending(),
            LocalDateTime.now(),
            new Address("街道1", "城市1", "省份1", "100000"),
            new Address("街道1", "城市1", "省份1", "100000")
        );
        
        System.out.println("传统对象大小: " + estimateSize(traditionalOrder));
        System.out.println("记录对象大小: " + estimateSize(recordOrder));
    }
    
    private static long estimateSize(Object obj) {
        // 简化的对象大小估算
        return 0; // 实际实现需要使用Instrumentation或其他工具
    }
}

总结

Java记录类和密封类的结合为构建现代、类型安全的领域模型提供了强大的工具。记录类通过减少样板代码和确保不可变性,提升了代码的简洁性和可靠性;密封类则通过编译时类型检查,确保了领域模型的完整性和正确性。

在实际的电商订单系统案例中,我们展示了如何利用这些特性构建健壮的领域驱动设计架构。通过模式匹配、领域事件和不可变对象的设计,我们能够创建出既易于理解又易于维护的系统。

随着Java语言的持续演进,记录类和密封类将成为构建现代Java应用程序的核心工具。建议开发团队积极采用这些特性,并结合实际业务场景进行深入实践,以充分发挥其优势。

Java记录类与密封类实战:构建不可变领域模型的最佳实践
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 java Java记录类与密封类实战:构建不可变领域模型的最佳实践 https://www.taomawang.com/server/java/1284.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务