Files

148 lines
4.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
paths:
- "**/*.java"
---
# Java 模式
> 本文档扩展了 [common/patterns.md](../common/patterns.md) 中的内容,增加了 Java 特有的部分。
## 仓储模式
将数据访问封装在接口之后:
```java
public interface OrderRepository {
Optional<Order> findById(Long id);
List<Order> findAll();
Order save(Order order);
void deleteById(Long id);
}
```
具体的实现类处理存储细节JPA、JDBC、用于测试的内存存储等
## 服务层
业务逻辑放在服务类中;保持控制器和仓储层的精简:
```java
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
public OrderService(OrderRepository orderRepository, PaymentGateway paymentGateway) {
this.orderRepository = orderRepository;
this.paymentGateway = paymentGateway;
}
public OrderSummary placeOrder(CreateOrderRequest request) {
var order = Order.from(request);
paymentGateway.charge(order.total());
var saved = orderRepository.save(order);
return OrderSummary.from(saved);
}
}
```
## 构造函数注入
始终使用构造函数注入 —— 绝不使用字段注入:
```java
// GOOD — constructor injection (testable, immutable)
public class NotificationService {
private final EmailSender emailSender;
public NotificationService(EmailSender emailSender) {
this.emailSender = emailSender;
}
}
// BAD — field injection (untestable without reflection, requires framework magic)
public class NotificationService {
@Inject // or @Autowired
private EmailSender emailSender;
}
```
## DTO 映射
使用记录record作为 DTO。在服务层/控制器边界进行映射:
```java
public record OrderResponse(Long id, String customer, BigDecimal total) {
public static OrderResponse from(Order order) {
return new OrderResponse(order.getId(), order.getCustomerName(), order.getTotal());
}
}
```
## 建造者模式
用于具有多个可选参数的对象:
```java
public class SearchCriteria {
private final String query;
private final int page;
private final int size;
private final String sortBy;
private SearchCriteria(Builder builder) {
this.query = builder.query;
this.page = builder.page;
this.size = builder.size;
this.sortBy = builder.sortBy;
}
public static class Builder {
private String query = "";
private int page = 0;
private int size = 20;
private String sortBy = "id";
public Builder query(String query) { this.query = query; return this; }
public Builder page(int page) { this.page = page; return this; }
public Builder size(int size) { this.size = size; return this; }
public Builder sortBy(String sortBy) { this.sortBy = sortBy; return this; }
public SearchCriteria build() { return new SearchCriteria(this); }
}
}
```
## 使用密封类型构建领域模型
```java
public sealed interface PaymentResult permits PaymentSuccess, PaymentFailure {
record PaymentSuccess(String transactionId, BigDecimal amount) implements PaymentResult {}
record PaymentFailure(String errorCode, String message) implements PaymentResult {}
}
// Exhaustive handling (Java 21+)
String message = switch (result) {
case PaymentSuccess s -> "Paid: " + s.transactionId();
case PaymentFailure f -> "Failed: " + f.errorCode();
};
```
## API 响应封装
统一的 API 响应格式:
```java
public record ApiResponse<T>(boolean success, T data, String error) {
public static <T> ApiResponse<T> ok(T data) {
return new ApiResponse<>(true, data, null);
}
public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(false, null, message);
}
}
```
## 参考
有关 Spring Boot 架构模式,请参见技能:`springboot-patterns`
有关实体设计和查询优化,请参见技能:`jpa-patterns`