Java 的 public/private/protected 機制簡單易懂,難點在於**架構層面的判斷**:哪些類應對哪些調用者隱藏?本文梳理四大典型場景,建立決策框架。
背景:大型系統 (如 ERP) 按業務域劃分模組 (採購、庫存、財務),各團隊並行開發。
問題:模組 A 需要模組 B 的數據 (如供應商付款條件),直接查表或調用 B 內部類 -> 耦合爆炸、Schema 變更連鎖崩潰。
解法:模組 B 定義 **Service 介面** (VendorService) 放在 api 包,**實現類置於 internal 或 impl 包並設為 package-private**。模組 A 僅依賴介面,通過 DI 框架 (Spring/CDI) 注入。編譯期即阻斷對實現類的非法引用。
背景:多模組共用核心資料結構 (如 OrderRecord、InventorySnapshot),包含業務約束 (狀態流轉、金額精度)。
風險:各模組複製定義 -> 定義漂移;共用單類 -> 字段被隨意 set、業務方法被覆寫。
方案:
private final,提供 getter、領域方法 (如 calculateTotal())。final 禁止繼承,或用 record (Java 16+) 天然不可變。經典 J2EE / Spring 分層:Controller (Web) -> Service (Business) -> Repository (Data)。
封裝策略:
public (api 包),實現 package-private (impl 包)。@Autowired ServiceInterface。收益:Service 單測可 Mock Repository;前端技術棟遷移 (JSP -> React) 零影響 Service;核心演算法替換 (MRP -> AI) 僅換實現類。
場景:業務邏輯 V1 簡陋 (如定價、風控),但對外簽名已確定,後續將重構。
策略:發布介面 PricingEngine,提供 V1PricingEngineImpl (隱藏)。重構完成後替換為 V2PricingEngineImpl,**調用方零代碼變更**,僅更新 DI 配置或 SPI 實現。
| 問題 | 是 | 否 | 行動 |
|---|---|---|---|
| 是否被多個獨立模組/開發者調用? | 隱藏實現,暴露介面 | 評估下一條 | |
| 是否包含核心業務不變量/約束? | 封裝字段,暴露領域行為 | 評估下一條 | |
| 是否屬於分層架構中的特定層實現? | 層內隱藏,層間介面 | 評估下一條 | |
| 實現邏輯近期是否會大幅變更? | 隱藏實現,穩定介面 | 可考慮直接 public |
exports / opens 精準控制包可見性,編譯期/運行期雙重保障。classes().that().resideInAPackage("..impl..").should().notBeAccessed().fromOutside())。@VisibleForTesting 濫用、包私有類被外部引用等反模式。