claude-code/claude-zh/skills/java-coding-standards/SKILL.md

147 lines
4.8 KiB
Markdown
Raw 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.

---
name: java-coding-standards
description: 針對 Spring Boot 服務的 Java 編碼標準包含命名規範、不可變性、Optional 使用、串流 (Streams)、例外處理、泛型以及專案結構。
---
# Java 編碼標準 (Java Coding Standards)
適用於 Spring Boot 服務中可讀且易於維護的 Java (17+) 程式碼標準。
## 何時啟用
- 在 Spring Boot 專案中編寫或審查 Java 程式碼。
- 執行有關命名、不可變性或例外處理的慣例。
- 使用 Records、Sealed Classes 或模式匹配 (Pattern Matching) (Java 17+)。
- 審查 Optional、串流或泛型的使用。
- 規劃套件結構與專案佈局。
## 核心原則
- **清晰重於取巧**:程式碼應易於理解。
- **預設不可變**:盡可能減少共享的可變狀態。
- **儘早報錯 (Fail Fast)**:拋出具備明確語義的例外。
- **一致性**:保持命名與套件結構的一致性。
## 命名規範
```java
// ✅ 類別 (Classes) / RecordsPascalCase (大駝峰)
public class MarketService {}
public record Money(BigDecimal amount, Currency currency) {}
// ✅ 方法 / 欄位camelCase (小駝峰)
private final MarketRepository marketRepository;
public Market findBySlug(String slug) {}
// ✅ 常數UPPER_SNAKE_CASE (全大寫底線)
private static final int MAX_PAGE_SIZE = 100;
```
## 不可變性 (Immutability)
```java
// ✅ 優先使用 Records 與 final 欄位
public record MarketDto(Long id, String name, MarketStatus status) {}
public class Market {
private final Long id;
private final String name;
// 僅提供 Getters不提供 Setters
}
```
## Optional 的使用
```java
// ✅ find* 方法應回傳 Optional
Optional<Market> market = marketRepository.findBySlug(slug);
// ✅ 使用 map/flatMap 而非直接 get()
return market
.map(MarketResponse::from)
.orElseThrow(() -> new EntityNotFoundException("找不到指定的市場資料"));
```
## 串流 (Streams) 最佳實踐
```java
// ✅ 使用串流進行轉換,保持細節簡潔,避免過長的鏈結
List<String> names = markets.stream()
.map(Market::name)
.filter(Objects::nonNull)
.toList();
// ❌ 避免過於複雜的嵌套串流;為了清晰起見,迴圈有時是更好的選擇
```
## 例外處理 (Exceptions)
- 針對領域錯誤使用未檢查例外 (Unchecked Exceptions);將技術性例外封裝並補足上下文。
- 建立領域特定的例外(例如:`MarketNotFoundException`)。
- 避免使用廣泛的 `catch (Exception ex)`,除非是為了在中心位置重新拋出或記錄。
```java
throw new MarketNotFoundException(slug);
```
## 泛型與型別安全
- 避免使用原始型別 (Raw Types);務必宣告泛型參數。
- 在開發可重用的工具型函式時,優先考慮有界泛型 (Bounded Generics)。
```java
public <T extends Identifiable> Map<Long, T> indexById(Collection<T> items) { ... }
```
## 專案結構 (Maven/Gradle)
```
src/main/java/com/example/app/
config/ # 配置類別
controller/ # REST API 控制器
service/ # 業務邏輯服務
repository/ # 資料存取層
domain/ # 實體與領域模型
dto/ # 資料傳輸物件
util/ # 工具類別
src/main/resources/
application.yml
src/test/java/... (與 main 目錄結構映射)
```
## 格式與風格
- 專案內必須一致使用 2 個或 4 個空格進行縮排。
- 每個檔案僅定義一個公開的頂層型別。
- 保持方法短小且聚焦於單一功能;適時提取輔助方法 (Helpers)。
- 成員排序:常數、欄位、建構子、公開方法、受保護方法、私有方法。
## 應避免的程式碼壞味道 (Code Smells)
- **過長的參數列表**:應改用 DTO 或建構者模式 (Builder)。
- **深層嵌套**:優先使用提早回傳 (Early Returns)。
- **魔術數字**:應改用具名的常數。
- **靜態可變狀態**:優先考慮使用依賴注入 (Dependency Injection)。
- **安靜的 Catch 塊**:必須記錄日誌並採取行動,或重新拋出。
## 日誌紀錄 (Logging)
```java
private static final Logger log = LoggerFactory.getLogger(MarketService.class);
log.info("擷取市場資料 slug={}", slug);
log.error("擷取市場資料失敗 slug={}", slug, ex);
```
## 空值 (Null) 處理
- 僅在不可避免時接受 `@Nullable`;否則應預設使用 `@NonNull`
- 在輸入端使用 Bean Validation`@NotNull`, `@NotBlank`)。
## 測試期望
- 使用 JUnit 5 與 AssertJ 進行流暢的斷言 (Fluent Assertions)。
- 使用 Mockito 進行 Mock盡可能避免使用部分 Mock (Partial Mocks)。
- 追求確定性的測試;不要在測試中使用隱含的休眠 (Sleep)。
**請記住**:保持程式碼具備意圖性、強型別且可觀察。在證明有必要進行微觀技術優化之前,應優先優化維護性。