安全修改旧代码的第一步,是记录当前结果并分开处理结构与规则变更。特征测试描述的是已有行为,并不保证这项业务规则正确。
原理与设计判断
重构是在保持可观察行为的前提下改变内部结构。规则不清楚时,除了正常输入,也要记录分支边界、缺失值与失败路径。这里的旧函数会在客户类型为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日依据: 实现契约与说明的依据。运行输出另由本文所附实验确认。

