重构前的准备:代码坏味道识别与技术债务评估

说实话,我见过太多团队一上来就撸袖子改代码。结果呢?改到一半发现牵一发动全身,最后不得不回滚。嗯,重构前的准备工作,其实比重构本身更重要。

这一章,我们就聊聊怎么在动手之前,先把「病」诊断清楚。

代码坏味道:你闻到了吗?

「坏味道」这个词,最早是 Martin Fowler 在《重构》里提出的。说白了,就是代码里那些「虽然能跑,但让人不舒服」的地方。我个人的习惯是,每次接手一个老项目,第一件事不是看业务逻辑,而是先扫一遍代码结构——闻味道。

常见的坏味道有三种,我一个个说。

1. 过长函数

一个函数超过 20 行,你就该警惕了。超过 50 行,基本可以断定它做了太多事。我在项目中遇到过最夸张的一个函数,足足 800 行——从参数校验、业务计算、日志打印到数据库写入,全塞在一起。

怎么判断?看缩进。如果一个函数里出现了三层以上的缩进嵌套,那就是警报。

// 坏味道示例:过长函数
public void processOrder(Order order) {
    // 参数校验
    if (order == null || order.getItems() == null || order.getItems().isEmpty()) {
        throw new IllegalArgumentException("订单无效");
    }
    // 计算总价
    double total = 0;
    for (OrderItem item : order.getItems()) {
        if (item.getPrice() < 0) {
            throw new IllegalArgumentException("价格不能为负");
        }
        total += item.getPrice() * item.getQuantity();
    }
    // 应用折扣
    if (order.getCoupon() != null) {
        total = total * (1 - order.getCoupon().getDiscountRate());
    }
    // 保存订单
    order.setTotal(total);
    orderDao.save(order);
    // 发送通知
    notificationService.send(order);
}

你看,一个函数干了五件事。拆成五个小函数,每个只做一件事,可读性会好很多。

我的经验: 拆函数的黄金法则是「单一职责」。如果一个函数需要你用「然后」「接着」「同时」来描述它的行为,那就该拆了。

2. 过大的类

类太大,通常意味着它承担了太多职责。我见过一个叫 OrderService 的类,里面塞了订单创建、支付、退款、物流、评价……几乎覆盖了整个电商流程。你想想看,这样的类改起来多痛苦?

判断标准很简单:一个类超过 300 行,或者包含了超过 10 个方法,就该考虑拆分了。更直观的指标是——你能不能在一分钟内说清楚这个类「到底负责什么」?

指标 健康 警告 危险
类行数 < 200 200 - 500 > 500
方法数 < 10 10 - 20 > 20
字段数 < 5 5 - 10 > 10
职责数 1 2 - 3 > 3

3. 重复代码

重复代码是技术债务的「万恶之源」。你想想看,同样的逻辑散落在三个地方,改需求的时候漏了一个,线上就出 bug。我曾经在一个项目里,发现同一个金额计算逻辑被复制粘贴了七次——每次还都改了一点点,导致维护成本翻了好几倍。

重复代码有三种常见形态:

  • 完全重复: 一模一样的代码块,直接提取成方法
  • 结构重复: 逻辑相似,只有参数不同,用模板方法或策略模式解决
  • 语义重复: 实现方式不同但功能相同,比如手动计算和调用工具类算同一个东西
避坑指南: 我曾经犯过一个错误——看到重复代码就急着抽公共方法。结果抽出来的方法参数越来越多,最后变成了一个「万能方法」,比原来的重复代码还难维护。记住:抽方法之前,先想清楚「这个公共逻辑真的不变吗?」

技术债务评估:到底欠了多少?

坏味道是「症状」,技术债务是「病因」。评估技术债务,不能光靠感觉。我习惯用四个维度来量化:

  1. 影响范围: 这段代码被多少模块依赖?改它会影响多少人?
  2. 修复成本: 重构需要多少工时?风险有多大?
  3. 维护成本: 不改的话,每次迭代要多花多少时间?
  4. 业务价值: 重构后能带来什么收益?性能提升?可扩展性?

每个维度打分 1-5 分,然后算加权总分。我一般用这个公式:

债务优先级 = 影响范围 × 0.3 + 维护成本 × 0.3 + 修复成本 × 0.2 + 业务价值 × 0.2

分数越高,越该优先处理。

重构优先级矩阵:先动哪里?

有了评估分数,就可以画矩阵了。横轴是「修复成本」,纵轴是「业务价值」。四个象限:

  • 第一象限(高价值 + 低成本): 立刻动手。比如抽一个重复的常量、拆一个过长函数。
  • 第二象限(高价值 + 高成本): 规划好再动手。比如重构核心模块的架构。
  • 第三象限(低价值 + 低成本): 顺手做。比如改个变量名、删个无用 import。
  • 第四象限(低价值 + 高成本): 先放着。比如重写一个很少改动的遗留模块。
核心原则: 重构不是为了「完美」,而是为了「更高效」。别在低价值的事情上浪费精力。

下面这张图,是我自己一直在用的评估框架:

重构优先级矩阵 修复成本 → 业务价值 → 高价值 · 低成本 高价值 · 高成本 低价值 · 低成本 低价值 · 高成本 抽重复常量 拆过长函数 核心模块重构 接口协议升级 修变量命名 遗留系统重写 优先处理「高价值 · 低成本」的坏味道,逐步积累重构信心

有了这个矩阵,你就能清楚地知道:哪些代码今天就要改,哪些可以放到下个迭代,哪些干脆别碰。

一个小技巧: 每次迭代留出 20% 的时间做「顺手重构」。专门排一个重构迭代,往往阻力很大。但如果是「改需求的时候顺手重构」,老板和产品经理都不会有意见。

好了,诊断工具准备好了。下一件事就是——怎么动手重构?别急,我们先把「坏味道」的清单列清楚,再谈具体手法。