安全修改舊程式碼的第一步,是記錄目前結果並分開處理結構與政策變更。特徵測試描述的是既有行為,並不保證這項業務規則正確。
原理與設計判斷
重構是在維持可觀察行為的前提下改變內部結構。規則不清楚時,除了正常輸入,也要記錄分支邊界、缺值與失敗路徑。此處的既有函式會在顧客類型為B且金額為正時扣除30。 第一手資料
重現程式碼
這個為實驗設計的折扣函式,改用折扣運算式實作。九個案例涵蓋-1、0、1、29、30、31、100與其他顧客類型。預期值另外寫在表內,不是再次呼叫舊函式來產生,因此能獨立檢查明確列出的契約。
這個獨立範例已於 2026-10-23 使用 Apple Swift 6.4,以 Swift 6 語言模式在 arm64 macOS 目標環境執行。不需要網路或額外套件。
請在下載檔案的資料夾執行。檢查不符時,precondition 會中止程式。請勿使用 -Ounchecked,因為它會移除這些檢查。
swift -swift-version 6 legacy-code-maintenance-problems.swift
func legacyPrice(kind: String, amount: Int) -> Int {
if kind == "B" && amount > 0 { return amount - 30 }
return amount
}
func refactoredPrice(kind: String, amount: Int) -> Int {
let discount = kind == "B" && amount > 0 ? 30 : 0
return amount - discount
}
let cases: [(String, Int, Int)] = [
("B", -1, -1), ("B", 0, 0), ("B", 1, -29),
("B", 29, -1), ("B", 30, 0), ("B", 31, 1),
("B", 100, 70), ("A", 100, 100), ("A", 0, 0)
]
var clampDifferences = 0
for (kind, amount, expected) in cases {
precondition(legacyPrice(kind: kind, amount: amount) == expected)
precondition(refactoredPrice(kind: kind, amount: amount) == expected)
let changed = kind == "B" && amount > 0 ? max(0, amount - 30) : amount
if changed != expected { clampDifferences += 1 }
}
precondition(clampDifferences == 2)
print("cases=9 preserved=9 clamp-differences=2")
執行結果與解讀
cases=9 preserved=9 clamp-differences=2
兩種實作都符合九個預期結果。若把負的總額調整為0,金額1與29的結果就會改變。clamp-differences=2不是掩蓋測試失敗,而是另外確認這項政策變更會改動兩個既有結果。
適用範圍與限制
這不表示應允許負的收費金額。實際折扣政策須向負責人確認。本例只用小整數,沒有驗證稅金、貨幣四捨五入、溢位或資料庫副作用。九個案例也無法證明所有輸入都等價。
接下來要確認的事
實務上先檢查呼叫端與儲存資料,補充觀察案例。若應禁止負金額,先議定新的預期值,再另外修改。若資料庫讓測試難以隔離,可接著參考儲存介面的依賴注入實驗。
來源與驗證
- Definition of RefactoringMartin Fowler · 作者原文 · 查核 2026年10月23日依據: 實作契約與說明的依據。執行輸出另外透過本文所附實驗確認。

