Java记录类与模式匹配实战:构建类型安全的领域模型 | Java现代化开发

2025-10-08 0 848

1. 记录类的革命性意义

Java 14引入的记录类(Record)彻底改变了我们定义数据载体的方式。它不仅仅是语法糖,更是向函数式编程和不可变设计理念的重要迈进。

1.1 传统JavaBean vs 记录类

// 传统的JavaBean方式
public class Person {
    private final String name;
    private final int age;
    private final String email;
    
    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
    
    // 大量的getter、equals、hashCode、toString方法...
}

// 记录类方式
public record Person(String name, int age, String email) {
    // 编译器自动生成所有必要方法
}

1.2 记录类的进阶用法

// 自定义验证逻辑
public record Email(String value) {
    public Email {
        if (value == null || !value.contains("@")) {
            throw new IllegalArgumentException("无效的邮箱地址");
        }
    }
}

// 静态工厂方法
public record Currency(String code, String symbol) {
    public static Currency of(String code) {
        return switch (code) {
            case "USD" -> new Currency("USD", "$");
            case "EUR" -> new Currency("EUR", "€");
            case "CNY" -> new Currency("CNY", "¥");
            default -> throw new IllegalArgumentException("不支持的货币代码");
        };
    }
}

2. 模式匹配的威力

模式匹配与记录类的结合,为Java带来了全新的数据处理范式。

2.1 instanceof模式匹配

// 传统方式
if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.length());
}

// 模式匹配方式
if (obj instanceof String str) {
    System.out.println(str.length());
}

2.2 switch表达式与模式匹配

public sealed interface Shape 
    permits Circle, Rectangle, Triangle {
    
    double area();
}

public record Circle(double radius) implements Shape {
    public double area() {
        return Math.PI * radius * radius;
    }
}

public record Rectangle(double width, double height) implements Shape {
    public double area() {
        return width * height;
    }
}

public record Triangle(double base, double height) implements Shape {
    public double area() {
        return base * height / 2;
    }
}

public class ShapeProcessor {
    public static String describe(Shape shape) {
        return switch (shape) {
            case Circle c -> String.format("圆形,半径: %.2f", c.radius());
            case Rectangle r -> String.format("矩形,宽: %.2f, 高: %.2f", 
                r.width(), r.height());
            case Triangle t -> String.format("三角形,底: %.2f, 高: %.2f", 
                t.base(), t.height());
        };
    }
}

3. 构建电商领域模型

我们将使用记录类和模式匹配构建一个完整的电商系统领域模型。

3.1 核心领域对象

// 值对象
public record Money(BigDecimal amount, Currency currency) {
    public Money {
        if (amount.compareTo(BigDecimal.ZERO)  stockQuantity) {
            throw new IllegalArgumentException("库存不足");
        }
        return new Product(id, name, description, price, 
            stockQuantity - quantity, status);
    }
}

public record ProductId(String value) {
    public ProductId {
        if (value == null || value.trim().isEmpty()) {
            throw new IllegalArgumentException("商品ID不能为空");
        }
    }
}

public enum ProductStatus {
    ACTIVE, INACTIVE, OUT_OF_STOCK, DISCONTINUED
}

3.2 订单聚合根

public record Order(
    OrderId id,
    CustomerId customerId,
    List items,
    OrderStatus status,
    Money totalAmount,
    LocalDateTime createdAt,
    Address shippingAddress
) {
    public Order {
        // 紧凑构造器中的验证逻辑
        if (items == null || items.isEmpty()) {
            throw new IllegalArgumentException("订单必须包含商品");
        }
        if (totalAmount.amount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("订单金额必须大于0");
        }
    }
    
    public static Order create(CustomerId customerId, 
                              List items,
                              Address shippingAddress) {
        var total = items.stream()
            .map(OrderItem::subtotal)
            .reduce(Money::add)
            .orElseThrow();
            
        return new Order(
            OrderId.generate(),
            customerId,
            List.copyOf(items),
            OrderStatus.CREATED,
            total,
            LocalDateTime.now(),
            shippingAddress
        );
    }
    
    public Order cancel() {
        if (status != OrderStatus.CREATED && status != OrderStatus.PAID) {
            throw new IllegalStateException("订单无法取消");
        }
        return new Order(id, customerId, items, OrderStatus.CANCELLED, 
            totalAmount, createdAt, shippingAddress);
    }
}

public record OrderItem(
    ProductId productId,
    String productName,
    int quantity,
    Money unitPrice
) {
    public Money subtotal() {
        return unitPrice.multiply(BigDecimal.valueOf(quantity));
    }
}

4. 领域服务与业务规则

使用模式匹配实现复杂的业务规则和定价策略。

4.1 定价策略引擎

public sealed interface PricingStrategy 
    permits FixedDiscount, PercentageDiscount, BuyOneGetOne, TieredPricing {
    
    Money apply(Product product, int quantity);
}

public record FixedDiscount(Money discountAmount) implements PricingStrategy {
    public Money apply(Product product, int quantity) {
        return product.price().multiply(BigDecimal.valueOf(quantity))
            .add(discountAmount.multiply(BigDecimal.valueOf(-1)));
    }
}

public record PercentageDiscount(BigDecimal percentage) implements PricingStrategy {
    public Money apply(Product product, int quantity) {
        var discountMultiplier = BigDecimal.ONE
            .subtract(percentage.divide(BigDecimal.valueOf(100)));
        return product.price().multiply(BigDecimal.valueOf(quantity))
            .multiply(discountMultiplier);
    }
}

public class PricingEngine {
    private final List strategies;
    
    public PricingEngine(List strategies) {
        this.strategies = List.copyOf(strategies);
    }
    
    public PricingResult calculatePrice(Product product, int quantity, 
                                       CustomerType customerType) {
        var basePrice = product.price().multiply(BigDecimal.valueOf(quantity));
        var bestPrice = basePrice;
        PricingStrategy appliedStrategy = null;
        
        for (var strategy : strategies) {
            var candidatePrice = strategy.apply(product, quantity);
            if (candidatePrice.amount().compareTo(bestPrice.amount())  bestPrice.multiply(BigDecimal.valueOf(0.9));
            case PREMIUM -> bestPrice.multiply(BigDecimal.valueOf(0.95));
            case REGULAR -> bestPrice;
        };
        
        return new PricingResult(basePrice, finalPrice, appliedStrategy);
    }
}

public record PricingResult(
    Money originalPrice,
    Money finalPrice,
    PricingStrategy appliedStrategy
) {
    public Money discountAmount() {
        return originalPrice.add(finalPrice.multiply(BigDecimal.valueOf(-1)));
    }
}

5. 事件驱动的领域架构

结合记录类和模式匹配构建响应式领域事件系统。

5.1 领域事件定义

public sealed interface DomainEvent 
    permits OrderCreated, OrderPaid, OrderShipped, OrderCancelled, 
            ProductStockReduced, ProductPriceChanged {
    
    String aggregateId();
    LocalDateTime occurredOn();
}

public record OrderCreated(
    OrderId orderId,
    CustomerId customerId,
    Money totalAmount,
    LocalDateTime occurredOn
) implements DomainEvent {
    public OrderCreated(Order order) {
        this(order.id(), order.customerId(), order.totalAmount(), 
             LocalDateTime.now());
    }
    
    public String aggregateId() { return orderId().value(); }
}

public record OrderPaid(
    OrderId orderId,
    String paymentId,
    Money paidAmount,
    LocalDateTime occurredOn
) implements DomainEvent {
    public String aggregateId() { return orderId().value(); }
}

// 事件处理器
public class DomainEventProcessor {
    private final Map<Class, 
                     List<EventHandler>> handlers;
    
    public DomainEventProcessor() {
        this.handlers = new ConcurrentHashMap();
    }
    
    public  void registerHandler(
        Class eventType, EventHandler handler) {
        handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList())
               .add(handler);
    }
    
    public void process(DomainEvent event) {
        var eventHandlers = handlers.get(event.getClass());
        if (eventHandlers != null) {
            eventHandlers.forEach(handler -> handleEvent(handler, event));
        }
    }
    
    @SuppressWarnings("unchecked")
    private  void handleEvent(
        EventHandler handler, DomainEvent event) {
        try {
            ((EventHandler) handler).handle((T) event);
        } catch (Exception e) {
            // 处理异常,确保不影响其他处理器
            System.err.println("处理事件失败: " + e.getMessage());
        }
    }
}

5.2 事件处理器的模式匹配实现

public class OrderEventHandler {
    public void handleDomainEvent(DomainEvent event) {
        switch (event) {
            case OrderCreated e -> handleOrderCreated(e);
            case OrderPaid e -> handleOrderPaid(e);
            case OrderShipped e -> handleOrderShipped(e);
            case OrderCancelled e -> handleOrderCancelled(e);
            case ProductStockReduced e -> handleStockReduced(e);
            case ProductPriceChanged e -> handlePriceChanged(e);
            default -> System.out.println("未知事件类型: " + event.getClass().getSimpleName());
        }
    }
    
    private void handleOrderCreated(OrderCreated event) {
        System.out.printf("订单创建: %s, 金额: %s%n", 
            event.orderId().value(), event.totalAmount());
        // 发送通知、更新读模型等
    }
    
    private void handleOrderPaid(OrderPaid event) {
        System.out.printf("订单支付: %s, 支付ID: %s%n", 
            event.orderId().value(), event.paymentId());
        // 触发发货流程
    }
    
    // 其他事件处理方法...
}

6. 测试策略与验证

利用记录类的不可变特性构建可靠的测试套件。

6.1 基于属性的测试

public class OrderTest {
    @Test
    void orderTotalShouldEqualSumOfItems() {
        // 给定
        var product1 = new Product(
            new ProductId("p1"), "商品1", "描述1", 
            new Money(new BigDecimal("100.00"), Currency.of("CNY")), 
            10, ProductStatus.ACTIVE
        );
        
        var product2 = new Product(
            new ProductId("p2"), "商品2", "描述2", 
            new Money(new BigDecimal("50.00"), Currency.of("CNY")), 
            5, ProductStatus.ACTIVE
        );
        
        var items = List.of(
            new OrderItem(product1.id(), product1.name(), 2, product1.price()),
            new OrderItem(product2.id(), product2.name(), 1, product2.price())
        );
        
        // 当
        var order = Order.create(new CustomerId("cust1"), items, 
            new Address("中国", "北京", "某区", "某街道"));
        
        // 那么
        var expectedTotal = new Money(new BigDecimal("250.00"), Currency.of("CNY"));
        assertEquals(expectedTotal, order.totalAmount());
    }
    
    @Test
    void orderCancellationShouldPreserveImmutability() {
        var originalOrder = createSampleOrder();
        var cancelledOrder = originalOrder.cancel();
        
        // 原始订单不应被修改
        assertEquals(OrderStatus.CREATED, originalOrder.status());
        assertEquals(OrderStatus.CANCELLED, cancelledOrder.status());
        
        // 其他字段应该保持不变
        assertEquals(originalOrder.id(), cancelledOrder.id());
        assertEquals(originalOrder.items(), cancelledOrder.items());
    }
}

7. 性能优化与最佳实践

7.1 记录类的性能优势

public class RecordPerformance {
    public static void main(String[] args) {
        var iterations = 1_000_000;
        
        // 测试记录类的实例创建性能
        long recordStart = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            var person = new Person("姓名" + i, i % 100, "email" + i + "@test.com");
        }
        long recordTime = System.currentTimeMillis() - recordStart;
        
        // 测试传统类的实例创建性能
        long traditionalStart = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            var person = new TraditionalPerson("姓名" + i, i % 100, "email" + i + "@test.com");
        }
        long traditionalTime = System.currentTimeMillis() - traditionalStart;
        
        System.out.printf("记录类耗时: %dms%n", recordTime);
        System.out.printf("传统类耗时: %dms%n", traditionalTime);
        System.out.printf("性能提升: %.2f%%%n", 
            (traditionalTime - recordTime) * 100.0 / traditionalTime);
    }
}

7.2 内存占用分析

public class MemoryAnalysis {
    public static void analyzeMemoryUsage() {
        Runtime runtime = Runtime.getRuntime();
        
        // 记录类内存使用
        runtime.gc();
        long recordMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
        List records = new ArrayList();
        for (int i = 0; i < 100000; i++) {
            records.add(new Person("Name" + i, 25, "test@example.com"));
        }
        long recordMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
        
        // 传统类内存使用
        runtime.gc();
        long traditionalMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
        List traditional = new ArrayList();
        for (int i = 0; i < 100000; i++) {
            traditional.add(new TraditionalPerson("Name" + i, 25, "test@example.com"));
        }
        long traditionalMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
        
        System.out.printf("记录类内存占用: %d bytes%n", 
            recordMemoryAfter - recordMemoryBefore);
        System.out.printf("传统类内存占用: %d bytes%n", 
            traditionalMemoryAfter - traditionalMemoryBefore);
    }
}

总结

Java记录类和模式匹配的结合为现代Java开发带来了重大改进:

  • 显著减少样板代码,提高开发效率
  • 内置不可变性,增强线程安全性
  • 模式匹配提供更清晰的条件逻辑表达
  • 更好的性能表现和内存效率
  • 领域驱动设计理念完美契合

这些特性使得Java在构建复杂业务系统时更加安全、高效和可维护。开发者应该积极采用这些现代Java特性,以构建更健壮的应用程序。

Java记录类与模式匹配实战:构建类型安全的领域模型 | Java现代化开发
收藏 (0) 打赏

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

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

淘吗网 java Java记录类与模式匹配实战:构建类型安全的领域模型 | Java现代化开发 https://www.taomawang.com/server/java/1179.html

常见问题

相关文章

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

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