147 lines
4.8 KiB
Markdown
147 lines
4.8 KiB
Markdown
|
|
---
|
|||
|
|
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) / Records:PascalCase (大駝峰)
|
|||
|
|
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)。
|
|||
|
|
|
|||
|
|
**請記住**:保持程式碼具備意圖性、強型別且可觀察。在證明有必要進行微觀技術優化之前,應優先優化維護性。
|