特例物件只適合合法的空狀態,不適合查詢失敗
Introduce Special Case:把特殊值做成同形狀的物件 的好處是呼叫端不用再檢查。但同一個機制也會把「出錯」偽裝成「正常的資料」。
// 沒有特例物件
const customer = site.customer // null
customer.name // 💥 TypeError,當場爆在出事的那一行
// 有特例物件
const customer = site.customer // UnknownCustomer
customer.name // 'occupant' —— 一路往下跑,什麼都沒發生問題不在「回了預設值」,而在預設值長得很合理,所以錯誤不會停下來,它會變成資料流下去:
- 帳單照 basic plan 開出去 → 幾個月後才有人發現這個客戶一直沒被正確收費
- 報表把它算進「0 週逾期」→ 整份逾期統計被稀釋,而且數字看起來很正常,沒人會去查
- 信件真的寄給
occupant了
而 null 會在第一行就炸,stack trace 直接指著出事的地方。
判準:這個特例是「狀態」還是「失敗」
| 情況 | 適合特例物件嗎 |
|---|---|
| 這個 site 真的沒有客戶(空屋)——業務上合法的狀態 | ✅ 這就是這招的原意 |
| 我查不到這個客戶(id 錯、資料遷移漏了、API 掛了) | ❌ 把一個該吵的失敗變成安靜的錯資料 |
麻煩的是這兩者很容易混在同一個 factory 裡:site.customer === 'unknown' 這個判斷本身分不出「空屋」和「查詢失敗」。
處方
查不到要讓它爆、或回明確的錯誤;只有業務上合法的空狀態才給特例物件。
兩者若共用同一個欄位,先把它們分開,再談要不要重構。
同一個「別讓錯誤安靜」的主題:在共用 endpoint 加無條件檢查會打爛其他呼叫者