十年前我刚入行时,总把代码重构当作简单的"整理代码"——无非是改改名、分分类、调调格式。直到有次在深夜加班修复一个由糟糕代码引发的线上故障时,我的导师指着满屏的if-else对我说:"知道为什么你总在救火吗?因为你把外科手术当成了家政服务。"
这句话彻底改变了我对重构的理解。真正的代码重构(Refactoring)是在不改变外部行为的前提下,通过调整内部结构来改善代码质量的技术手段。就像给建筑做结构加固,既要保证住户正常生活,又要提升整体抗震等级。
新手常混淆的两个概念:
我曾参与过一个电商促销系统改造。原代码是典型的"祖传代码"——5年间经手过12个程序员,每个需求都通过新增if-else实现。团队最初打算重写,但评估后发现:
最终我们采用渐进式重构:
java复制// 改造前
public double calculateDiscount(User user, Product product) {
// 200多行if-else
}
// 第一步:策略模式封装
public interface DiscountStrategy {
boolean match(User user, Product product);
double getDiscount();
}
// 第二步:拆分具体策略类
public class VipDiscount implements DiscountStrategy { ... }
public class NewUserDiscount implements DiscountStrategy { ... }
// 第三步:上下文组装
public class DiscountContext {
private List<DiscountStrategy> strategies;
public double applyDiscount(User u, Product p) {
for (DiscountStrategy s : strategies) {
if (s.match(u, p)) return s.getDiscount();
}
return 1.0;
}
}
关键经验:当遇到"屎山代码"时,先用重构摸清业务逻辑,再考虑局部重写。就像考古修复文物,得先清理表面污垢才能看清原始纹路。
我总结的实操守则:
实际操作时的checklist:
markdown复制- [ ] 原有测试用例全部通过
- [ ] 新增测试覆盖修改部分
- [ ] 代码diff不超过200行
- [ ] 提交信息注明重构类型(如"提取方法"、"引入策略模式")
原始代码:
python复制class Order:
def __init__(self):
self.status = "created"
def handle_event(self, event):
if self.status == "created":
if event == "pay":
self.status = "paid"
# 触发支付后续操作
elif event == "cancel":
self.status = "cancelled"
# 清理库存占用
elif self.status == "paid":
if event == "ship":
self.status = "shipped"
# 通知物流系统
# 更多if-else...
重构步骤:
python复制class OrderState(ABC):
@abstractmethod
def handle(self, order, event): pass
python复制class CreatedState(OrderState):
def handle(self, order, event):
if event == "pay":
order.change_state(PaidState())
# 支付相关操作
elif event == "cancel":
order.change_state(CancelledState())
python复制class Order:
def __init__(self):
self.state = CreatedState()
def change_state(self, new_state):
self.state = new_state
def handle_event(self, event):
self.state.handle(self, event)
避坑指南:状态模式重构时最容易犯的错误是忘记处理所有状态转换。建议先用单元测试枚举所有可能的[状态×事件]组合。
有次我优化一个耗时较长的报表生成代码:
javascript复制// 原始版本(可读性好但性能差)
function generateReport() {
return fetchData()
.then(cleanData)
.then(calculateStats)
.then(formatOutput);
}
// 优化版本(性能提升但嵌套较深)
async function generateReport() {
const rawData = await fetchData();
const cleaned = await cleanData(rawData);
const stats = await calculateStats(cleaned);
return formatOutput(stats);
}
最终采用的平衡方案:
javascript复制async function generateReport() {
const [rawData, templates] = await Promise.all([
fetchData(),
loadTemplates()
]);
const pipeline = pipe(
cleanData,
calculateStats,
curry(formatOutput)(templates)
);
return pipeline(rawData);
}
关键取舍点:
在改造一个老旧的CMS系统时,我运用DDD思想进行了分层重构:
原始结构
code复制controllers/
article.php
category.php
models/
Article.php
Category.php
重构后
code复制src/
Domain/
Article/
Entity/
Article.php
Slug.php
Repository/
ArticleRepository.php
Service/
PublishingService.php
Application/
Article/
Commands/
PublishArticleCommand.php
Queries/
GetPublishedArticlesQuery.php
Infrastructure/
Persistence/
DoctrineArticleRepository.php
Presentation/
ArticleController.php
经验之谈:领域驱动重构最难的不是技术实现,而是说服团队成员接受更复杂的目录结构。我的策略是先在小模块试点,用实际的可维护性提升来说服大家。
我踩过最痛的几个坑:
过度设计:为了用模式而用模式,反而增加复杂度
过早优化:在性能瓶颈不明时进行微优化
测试缺失:没有测试保护的重构如同蒙眼拆弹
我的决策流程图:
code复制是否理解现有代码?
├─ 否 → 先添加注释和测试
└─ 是 → 修改成本是否高于重写?
├─ 是 → 考虑局部重写
└─ 否 → 是否存在明确优化点?
├─ 是 → 立即重构
└─ 否 → 维持现状
最近的一个实战案例:在修改用户积分计算逻辑时,发现原有代码将积分规则硬编码在Service中。判断过程:
于是采用策略模式重构,为后续的会员体系升级预留了扩展点。
IntelliJ IDEA:
VS Code:
静态分析:
可视化工具:
测试辅助:
我的日常工作流:
bash复制# 1. 静态检查
npm run lint
# 2. 运行测试
pytest --cov
# 3. 提交前diff检查
git diff --word-diff
去年我带团队对一个运行了8年的核心系统进行持续重构,最终达成:
最让我自豪的不是这些数字,而是一个实习生的话:"现在看代码就像读一本好书,每个类都恰到好处地待在它该在的位置。"这或许就是对代码工匠最好的认可。