當類的構造函數未初始化所有字段,強制客戶代碼在構造後連續調用多個 setter 完成初始化,這種多步驟初始化稱為 Run-On Initialization。它極易導致:異常拋出時字段未初始化、初始化步驟遺漏/順序錯誤、類演進導致所有客戶代碼需同步修改(Rogue Tile 模式)。
RestrictedInt ri = new RestrictedInt(false);
ri.setValue(0); // 拋出 CantTakeZeroException,value 字段仍為 null
// 異常處理器訪問 ri.value → NPE,掩蓋真實錯誤
鐵律:構造函數必須初始化所有字段,建立完整有效對象。 若無法一次提供所有值,應提供多構造函數重載,而非暴露 setter。
value = NonValue.ONLY),避免 NPE。NonValue)代表「未初始化」,訪問時拋出帶上下文的異常(RuntimeException("Attempt to access uninitialized field")),優於 NPE。如 List 接口在 Empty 子類中實現 getFirst() 拋異常,而非讓客戶 instanceof 判斷。雖違背「接口只定義有效操作」直覺,但消除了大量類型轉換與 instanceof,提升性能與可讀性。關鍵在於:設計動機決定模式適用性。
Run-On Initialization 是構造函數職責不全的產物。新代碼堅持完整構造;舊代碼用默認類、非空值、狀態檢查法降低風險。下一期探討 Java 平台相關的「Gotcha」錯誤。