想象你买下一套住了二十年的老房子。墙壁里藏着锈蚀的水管,电路的负荷早就跟不上现代家电,阳台的裂缝在雨天悄悄渗水——房子还能住,但每一次改造都像走钢丝。软件代码也是这样:它能跑、能上线、能赚钱,但只要你碰它一下,就可能漏水断电。重构,就是软件世界的“老房翻新”手艺。 这篇内容会带你走一遍完整的翻新流程:先体检找病灶(坏味道识别),再去查工艺手册挑合适的施工方法(重构手法分类),控制好施工节奏避免把房子拆塌(节奏控制策略),每道工序做完都要验收(回归验证机制),最后把师傅们的经验沉淀成公司标准(团队规范制定)。你不需要懂编程语言细节,只需像理解装修一样理解代码翻新,就能掌握重构的完整方法论。

岗位关联

对 AI 从业者来说,模型代码、训练管道、推理服务同样是业务资产。掌握重构方法论,你才能在快速迭代中保持代码可读、可测、可演进,让团队质量和交付速度不因“技术债”而崩塌。

目录
  1. 可视化演示
  2. 坏味道识别
  3. 重构手法分类
  4. 节奏控制策略
  5. 回归验证机制
  6. 重构目录活用
  7. 团队规范制定
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

\u5c55\u793a\u51ef\u6492\u5bc6\u7801\u5c06\u660e\u6587HELLO\u7684\u6bcf\u4e2a\u5b57\u6bcd\u6309\u6307\u5b9a\u4f4d\u79fb\u91cf\u5faa\u73af\u79fb\u4f4d\u52a0\u5bc6\u7684\u8fc7\u7a0b\uff0c\u52a8\u6001\u53ef\u89c1\u5b57\u6bcd\u8868\u4e2d\u7684\u6620\u5c04\u5173\u7cfb\u548c\u5bc6\u6587\u7ed3\u679c\u3002

本节配有交互动效(进入视口后自动加载,约 3KB)。

\u51ef\u6492\u5bc6\u7801\u52a0\u5bc6 \u660e\u6587 H \u2192 \u5bc6\u6587 K \u5f53\u524d\u79fb\u4f4d+3 C=(P+k)%26 \u5b57\u6bcd\u8868\u5faa\u73af\u79fb\u4f4d\u52a0\u5bc6

坏味道识别

小林请来的装修师傅做的第一件事,不是刷墙,而是拿着手电筒和湿度计在房子里转了一整天。他指着墙角的斜裂缝说:“这不只是漆面问题,是地基沉降留下的结构隐患。”阳台角落里那根随意缠绕的电线,他拍了好几遍照片:“这里哪天跳闸,全屋都得断电。”装修开工前,必须先找出藏在墙里的“病灶”——代码世界也是如此。

这些“病灶”在重构术语中叫代码坏味道。它不是编译错误,程序运行得好好的,只是结构上散发着危险信号。最经典的四类坏味道是:长方法——一个函数动辄上百行,就像一间塞满杂物的客厅,想找个水杯都得翻半天;大类——一个类承担了用户管理、订单处理、日志输出等十几种职责,如同一个房间既当卧室又当厨房还兼书房;重复代码——几乎一样的逻辑散落在五六个地方,改一个漏一处,正如相同的水管接头漏水点出现多次;过长参数列表——方法参数排成一长串,像拖了一长串插线板,接错一个就出事。

识别坏味道不能只靠直觉,工程师引入了一个量化工具——圈复杂度。它衡量一个方法里有多少条独立路径:路径越多,越难读懂、越难测试。

当圈复杂度超过 10 时,方法就需要警惕;超过 20,几乎可以断定存在长方法坏味道。装修师傅的体检清单由此变成了一份可量化的报告:先摸清房子哪里开裂、哪里漏水,重构才能有的放矢。识别坏味道,就是给代码做“体检”,是每一次重构的起点。

v(G)=e−n+2p\displaystyle v(G) = e - n + 2p
圈复杂度公式就像“房间动线复杂度”:门和走廊越多,路越绕。v(G) 越大,说明分支越多,代码越容易藏 bug。
  1. v(G) 的含义圈复杂度值,表示一个方法中独立线性路径的数量,数值越高逻辑越复杂。
    v(G)v(G)
  2. e 的含义控制流图中“边”的数量,即从一个语句跳转到另一个语句的连线数量。
    ee
  3. n 的含义控制流图中“节点”的数量,每个节点代表一条顺序执行的语句或判断条件。
    nn
  4. p 的含义连通分量数量,通常为 1,表示整个图是一个整体。
    pp
  5. 整体计算用边数减去节点数,再加上两倍的连通分量数,就得到圈复杂度。
    e−n+2pe - n + 2p

重构手法分类

体检报告出来了,接下来选施工工艺。小林的师傅翻开一本厚厚的工艺图集:“裂缝要用碳纤维布加固,电线要重新穿管,阳台防水要铲掉重做。”不同病灶对应不同工艺,不能拿修补腻子去堵结构裂缝。代码重构同样有一整套“工艺手册”,Martin Fowler 把常见手法归纳为几大类。

组合方法是使用频率最高的一类,核心动作是“拆”和“提”。最经典的是提取函数:把长方法中一段逻辑独立成一个小函数,就像把大衣柜里的杂物分类装进五个收纳盒。反过来还有内联函数:当两个函数嵌套过深反而更难读时,把它们合并成一个,相当于把两堵薄隔墙打通成一间大房间。组合方法的宗旨是:每个方法都只做一件事,名字就能说明一切。

移动特性处理的是“放错位置”的责任。如果 A 类里的方法其实只依赖 B 类的数据,就应该把方法搬到 B 类中去,这叫搬移函数。同理,搬移字段把字段挪到它真正归属的类。这就好比把厨房里的洗衣机搬进卫生间——工具放在对的地方,干活才顺手。简化条件专门对付复杂的 if-else 嵌套,把多层判断改写成卫语句、多态或策略模式,让代码的“岔路口”一目了然;而组织数据则把散落的裸变量封装成对象,或把魔法数字替换成可读常量,如同给电线套上绝缘管、给每根水管贴上标签。

选择哪类手法,取决于坏味道的“病灶”在结构上属于哪一类:长方法找组合方法类,职责错位找移动特性类,逻辑绕弯找简化条件类,数据裸奔找组织数据类。工艺选对了,翻新才不会留下二次隐患。

节奏控制策略

真正开工后,师傅反复强调一句话:“不能一天拆完所有墙,也不能一次改完全屋水电。”老房子的结构脆弱,必须一面墙一面墙地改,每改完一小块就检查一次。小林一开始不理解:“为什么不干脆全拆了重装,不是更省事吗?”师傅摇头:“全拆重装的风险你承担不起,住着人的房子只能边住边改。”

这就是重构最重要的节奏原则——小步重构。每次只进行一个手法,改动范围控制在十几分钟能完成的程度,然后立刻编译、跑测试,确认绿灯再走下一步。千万不要把“重构”和“重写”混为一谈:重构改变结构、不改变行为,重写则推倒重来、风险剧增。另一条原则是重构与功能开发分离:给房子换水管的时候,不要顺手改房间布局;在提交代码时,一次提交只做一个类型的变更,让评审的人能看清每一步在干什么。

为什么节奏这么重要?因为有一种“利息”在时刻吞噬代码价值,那就是技术债务。当年为了赶上线而走捷径留下的烂结构,就是借了一笔钱;此后每次改需求、修 bug 都要为烂结构付“利息”——多花的时间、多踩的坑。这笔债务会按复利增长:

小步重构 + 及时还债,就像装修中“改一段、稳一段、验收一段”,让老房子始终处于可居住状态。即使中途被叫停,房子依然能正常用——这正是提升重构安全性的核心心法。

D(t)=D0×ert\displaystyle D(t) = D_0 \times e^{r t}
技术债务公式就像信用卡复利:欠得越久,利滚利越吓人。D(t) 是 t 时刻的债务总量,D₀ 是最初欠下的债,r 是利率(复杂度增长速度)。
  1. D(t) 的含义经过 t 时间后的技术债务总量,代表未来维护要多付出的成本。
    D(t)D(t)
  2. D₀ 的含义初始技术债务,即当初偷懒走捷径时欠下的“本金”。
    D0D_0
  3. e 的含义自然常数,约等于 2.718,用于描述连续复利增长过程。
    ee
  4. r 的含义债务利率,即代码复杂度随时间的增长速度,工程混乱度越高 r 越大。
    rr
  5. t 的含义时间长度,通常以迭代周期为单位。
    tt
  6. 整体含义技术债务不是线性增长,而是指数增长,越晚偿还,代价越沉重。
    D0×ertD_0 \times e^{r t}

回归验证机制

装修进行到水电改造阶段,师傅在墙上开了一排槽。小林有点慌:“这墙都快打通了,怎么能确定没破坏承重结构?”师傅淡定地拿起试压泵:“接上水管,打到八公斤压力,看它漏不漏;电路改完,每一路都摇表测绝缘。验收过了,才能封墙。”每一道工序后面,都跟着一道验收关卡——代码重构也是如此,这套关卡叫回归验证。

第一道关卡是单元测试。它把每个函数看作独立的“水龙头”,接上各种输入水源,检查输出的水流是否符合预期。重构前先跑一遍测试,建立“行为基线”;重构后再跑一遍,测试全绿,说明行为没变。测试用例越完善,重构时越有底气。第二道关卡是持续集成:每次提交代码都自动触发布建和全部测试,像装修队每天收工前的例行检查,任何一根“漏水管”都会在第一时间被系统报警。

第三道关卡是代码评审,其价值类似监理工程师:让另一位工程师从旁审视,检查节奏是否过猛、手法是否得当、是否有遗漏的场景。一个好的评审能补上测试覆盖不到的盲区。这些关卡共同构成测试金字塔:底层是大量快速细颗粒的单元测试,中层是少量集成测试,顶层是极少数的端到端测试。金字塔越稳,重构的“安全网”越密。

为什么测试数量如此重要?可以用一个概率公式说明:

有了这张由单元测试、持续集成、代码评审织成的安全网,重构就像在水电验收合格之后再封墙,贴着瓷砖也能安心。行为不变,结构变优——这就是回归验证守护的黄金准则。

P=1−(1−p)n\displaystyle P = 1 - (1 - p)^{n}
这个公式很像“多装几道防盗门”:一道门可能被撬开,n 道独立门全被撬开的概率就微乎其微。P 是整个测试集至少抓住一个回归 bug 的概率。
  1. P 的含义至少一个测试用例成功捕获回归缺陷的概率,数值越接近 1 越安全。
    PP
  2. p 的含义单个测试用例能捕获某个缺陷的概率,通常较小。
    pp
  3. (1-p) 的含义单个测试漏过该缺陷的概率。
    1−p1 - p
  4. n 的含义相关测试用例的数量,n 越大,漏网概率越低。
    nn
  5. 整体含义每个测试独立参与“抓捕”,全部漏掉才失败,所以只要测试足够多,回归风险就接近于零。
    (1−p)n(1 - p)^{n}

重构目录活用

装修到了一个有趣的时刻。小林发现,师傅几乎不需要思考就能决定“这里怎么改”。他随身带着一本自己整理的施工笔记,里面按“现象→做法”分类记录:墙面裂缝怎么处理、地漏反味怎么解决、窗台渗水用什么材料。遇到问题翻开查一下,照着标准操作,很少走弯路。Fowler 的《重构》一书,就是代码世界的“施工笔记”,即重构目录。

Fowler 目录的每个条目都遵循固定格式:名称、动机、做法、示例。比如提取函数的动机是“方法过长、需要注释才能看懂”,做法分五步:找到独立逻辑段、新建函数、把逻辑搬过去、检查变量作用域、替换原位置引用。之所以强调目录的价值,是因为有了目录,重构才从“看心情的直觉”变成“可查阅的工程规范”。

更关键的是坏味道到手法的映射关系。目录就像一本“病症-药方”对照表:看到长方法,先查“组合方法”类目;看到大类,查“移动特性”类目中的拆分类;看到 switch 语句成堆出现,翻到“简化条件”类目。工程师不需要每次重构都从零思考——遇到什么问题,翻到什么手法,按步骤实施,这就是目录使用的完整闭环。

但手册不会告诉你的是“何时该动手”。触发时机需要经验判断:当你需要加一个新功能却发现无从下手,说明结构在阻碍变化;当你修一个 bug 需要同时改动七八处,说明重复代码在蔓延;当代码评审里大家都在争论“这块逻辑应该放哪”,说明移动特性的时机到了。像师傅说的:“不是每天都要开工,但下雨天看到渗水,就是该补漏的日子。”目录是工具书,时机是判断力,两者结合,重构才真正高效。

团队规范制定

整个装修项目结束后,小林记录了一个细节:那个师傅不仅手艺好,而且每天收工都整理工具、填施工日志、和业主确认第二天工序。后来小林才知道,他所在的装修公司对每个工地都有统一的施工规范——隐蔽工程必须拍照留底,防水必须做 48 小时闭水试验,垃圾必须当日清运。个人手艺再好,没有团队标准,质量也无法复制。代码重构有着同样的规律。

代码评审制度是团队重构规范的第一支柱。把“重构”明确列为评审重点,每个人都检查:这次的改动是纯结构重构,还是夹带了功能变化?手法选择是否对应坏味道?修改范围是否控制在小步内?评审相当于装修公司的“监理复查”,让规范在每个 PR 中得到执行。将重构纳入迭代计划则是第二支柱:在制定迭代计划时,明确留出重构任务,而不是永远“等有空再改”。谁也不会指望装修师傅“有空顺便”把水管换了——同样的道理,重构任务必须被正式排期。

第三支柱是重构知识库建设。把团队遇到的坏味道、选择的手法和处理记录沉淀成文档,相当于把老师傅的经验变成公司的“工艺图集”。新人来了先读知识库,不必再踩同样的坑。第四支柱是重构文化——让“改善代码”成为团队默认价值观。工程师提出“这部分结构太绕,我想花一天时间整理”时,得到的回应应该是理解,而不是质疑。就像装修公司里,主动发现隐患并上报的师傅会被表扬,而不是被骂“多事”。

从个人手艺到团队标准,从项目末尾的“灾后重建”到迭代中的常态化维护,重构规范让质量改善不再是某个人的英雄主义,而是组织的系统能力。老房子会一直变老,但一套好的装修标准,能让它始终住得舒心。这件事,值得每个团队认真对待。

AI 应用场景

机器学习管道代码重构

阶段:模型开发与部署维护

解决的问题:训练代码通常以实验脚本起步,数据加载、特征工程、模型训练、评估逻辑堆在几个超长 notebook 或脚本中,团队成员难以复用,迭代一次实验要改五六个地方,属于典型的“长方法 + 重复代码”坏味道密集区。运用提取函数、搬移函数等重构手法,可以把管道拆分为数据清洗器、特征工厂、模型工厂、评估器四个职责独立的模块,行为不变而可复用性大增。

具体案例:某算法团队对一份 800 行的训练脚本做重构,先按“组合方法”提取出 build_dataloader、compute_metrics 等函数,再用“移动特性”把特征逻辑搬进 feature_engineering.py,最后补充单元测试锁定关键逻辑。重构后,新人接手时间从两周缩短到两天,实验迭代速度提升一倍。

AI 辅助代码评审与坏味道扫描

阶段:代码质量保障

解决的问题:人工评审依赖个人经验,容易漏掉隐藏的坏味道,而且每个人对“什么值得重构”的尺度不一。基于大语言模型构建坏味道扫描器,自动识别长方法、重复代码块、过长参数列表,并推荐对应重构手法,把评审从“凭感觉”升级为“按目录核查”。

具体案例:团队在 CI 流水线接入一条“重构建议”检查项,每次 PR 自动运行扫描,输出类似“train_model 圈复杂度 23,建议提取特征归一化逻辑”的提示。开发者在提交前先自查一遍,评审时聚焦核心设计,重构规范落地从“制度”变成了“工具”。

CI/CD 中的重构回归验证门禁

阶段:持续交付与质量门禁

解决的问题:重构最大的风险是“改了结构,弄坏了行为”。过去依赖人工判断是否要跑全量测试,既不及时也不可靠。将回归验证机制嵌入 CI/CD:每次提交自动运行单元测试、集成测试和静态检查,以绿灯作为合并的前置条件,重构再小也有安全网兜底。

具体案例:某团队为重构建了独立的 QA Pipeline:代码推送后自动跑测试金字塔(800 个单元测试 + 40 个集成测试),并在评审页面展示覆盖率变化曲线。一次重构导致 3 个测试失败,流水线立即阻断合并,开发者当场修复,避免了带病代码上线。

Python 演示

python80 行
import astfrom collections import Counter  class SmellDetector(ast.NodeVisitor):    """AST based code smell detector"""     def __init__(self, max_lines=20):        self.max_lines = max_lines        self.long_methods = []        self.duplicate_blocks = []        self.short_names = []     def visit_FunctionDef(self, node):        end_line = getattr(node, "end_lineno", node.lineno)        line_count = end_line - node.lineno + 1        if line_count > self.max_lines:            self.long_methods.append(                (node.name, line_count, node.lineno)            )        for item in node.body:            targets = self._extract_targets(item)            for name in targets:                if len(name) <= 1:                    self.short_names.append(                        (node.name, name, item.lineno)                    )        self.generic_visit(node)     def _extract_targets(self, stmt):        names = []        if isinstance(stmt, ast.Assign):            for t in stmt.targets:                if isinstance(t, ast.Name):                    names.append(t.id)        return names     def detect_duplicates(self, source):        tree = ast.parse(source)        funcs = [            (n.name, ast.get_source_segment(source, n))            for n in ast.walk(tree)            if isinstance(n, ast.FunctionDef)        ]        seen = Counter(seg for _, seg in funcs)        self.duplicate_blocks = []        added = set()        for name, seg in funcs:            if seen[seg] > 1 and seg not in added:                added.add(seg)                self.duplicate_blocks.append((name, seen[seg]))     def report(self):        out = []        out.append("== long methods ==")        for name, lines, lineno in self.long_methods:            out.append("  %s at line %d, %d lines" % (name, lineno, lines))        out.append("== short variable names ==")        for func, var, lineno in self.short_names[:10]:            out.append("  '%s' at line %d in %s" % (var, lineno, func))        out.append("== duplicate function bodies ==")        for name, count in self.duplicate_blocks:            out.append("  %s appears %d times" % (name, count))        return "\n".join(out)  if __name__ == "__main__":    import sys     target = sys.argv[1] if len(sys.argv) > 1 else "sample.py"    try:        with open(target, encoding="utf-8") as f:            source = f.read()    except FileNotFoundError:        print("file not found: " + target)        sys.exit(1)    detector = SmellDetector()    detector.visit(ast.parse(source))    detector.detect_duplicates(source)    print(detector.report())
目的:这是一个基于 Python AST(抽象语法树)的坏味道检测器,自动扫描源代码,识别三种常见坏味道:长方法(超过 20 行)、过短变量名(单个字符)、重复函数体。
  1. class SmellDetector(ast.NodeVisitor)继承 AST 访问器,用“遍历语法树”的方式逐一定位代码结构,就像装修师傅逐面墙检查裂缝。
  2. def visit_FunctionDef(self, node)NodeVisitor 的钩子方法,每遇到一个函数定义就会被自动调用,是实现长方法检测的核心入口。
  3. end_line = getattr(node, "end_lineno", node.lineno)兼容不同 Python 版本获取函数结束行的写法,end_lineno 缺失时回退到起始行。
  4. line_count = end_line - node.lineno + 1计算函数体总行数,这是“长方法”坏味道的量化指标。
  5. if line_count > self.max_lines:行数超过阈值 20 就记录到 long_methods 列表,对应重构中的坏味道识别环节。
  6. for item in node.body:遍历函数体内的每一条语句,检查是否有“可疑短变量名”。
  7. if isinstance(t, ast.Name)只统计赋值语句目标中真正的变量名,排除属性访问和元组解包。
  8. def detect_duplicates(self, source)用 ast.get_source_segment 提取每个函数体的源码文本,通过 Counter 统计哪些函数体一模一样。
  9. self.duplicate_blocks.append((name, seen[seg]))记录重复函数名及出现次数,对应“重复代码”坏味道的识别。
  10. detector.visit(ast.parse(source))解析源码并启动 AST 遍历;detector.detect_duplicates(source) 再独立完成重复代码检测,两步构成一次完整“体检”。

常见误区

误区「重构就是看着代码不顺眼,推倒重来,反正我有时间,重写一遍更干净。」

正解重构和重写有本质区别:重构是在保持行为不变的前提下改善结构,一次只做一步、每步都有测试托底;重写是放弃原有实现、从零开始,行为发生翻天覆地的变化,风险呈指数级上升。像装修老房子,换水管是重构,拆了重建是重写——前者还能住人,后者得先搬家。除非代码烂到完全无法测试,否则永远优先考虑小步重构。

为何常见:很多人看到烂代码的第一反应是“不如重写”,因为重写看起来更彻底、更有成就感,却忽略了其中巨大的隐性风险:原代码中积累的边界条件和业务知识会随重写而丢失。

误区「反正要重构,就周末加个班,把这块大模块的十多个功能一口气全改完。」

正解重构必须小步走。每次改动只用一个手法、只针对一个坏味道,改完立刻编译、跑测试。一次性大规模改动有几个致命问题:改动面太大导致无法定位回归错误;评审者难以理解变更内容;中途被打断时代码处于半成品状态。正确的做法像开香槟——先开瓶,再慢慢倒,而不是把瓶子摔碎。

为何常见:人的直觉倾向于“一步到位”,尤其是面对堆积如山的坏味道时,“一次解决所有问题”的诱惑极其强烈,但工程现实是节奏比速度更重要。

误区「我改的是结构,逻辑又没变,跑不跑测试都一样,没必要写什么单元测试。」

正解没有测试的重构就是蒙眼换电线。结构改动虽然不改变意图,但极容易在细节处改变行为——变量作用域弄错、边界条件漏掉、异常分支被吞掉。单元测试是重构的“安全网”,它先把当前行为固化成基线,重构后全绿说明行为没变,变红则说明某处改坏了。没有测试,就没有重构资格,这不是一句口号,而是行业共识。

为何常见:初学者往往高估自己的细心程度,觉得“逻辑没变就不会出错”,忽视了代码在移动、拆分、合并过程中大量隐蔽的语义变化。

误区「装了圈复杂度检查插件,让 CI 自动跑一遍,哪些代码有坏味道机器说了算。」

正解工具能提供量化线索(行数、复杂度、重复率),但无法替代工程师的语义判断。一个 30 行的方法如果内聚性强、职责单一,未必需要拆分;一个 15 行的方法如果塞了三条互不相干的逻辑,反而应该重构。工具是手电筒,照得出裂缝,但裂缝要不要补、怎么补,需要人基于业务上下文做判断,这正是重构目录和经验的用武之地。

为何常见:自动化检查能给出明确、客观、不需要思考的数字,让人产生“完成工作”的错觉;而真正的坏味道识别需要理解代码职责,这是一种需要刻意练习的工程判断力。

误区「重构是我的事,我把自己负责的模块改干净就行,不需要跟别人商量,也不需要什么流程。」

正解如果没有团队规范,张三重构过的代码李四看不懂,李四改的地方又和张三的风格冲突,质量改善无法积累成复利。代码评审制度、重构纳入迭代计划、知识库沉淀、文化认同——这四者共同保证重构是系统性能力而非个人习惯。一个人的手艺人模式,救不了整个组织的老房子。

为何常见:初入职场的工程师往往只关注“我手头的代码”,还没有建立“代码是团队资产”的认知,容易忽略协作边界和标准统一对长期质量的决定性作用。

面试题

Q1. 下列哪一项属于代码坏味道的典型例子?

单选题 · easy

  • A.长方法← 正确答案
  • B.变量命名
  • C.模块分层
  • D.接口设计

答案:A。长方法是 Martin Fowler 总结的经典坏味道之一,指方法过长、职责混杂;变量命名、模块分层、接口设计都是正常的工程概念本身,不是坏味道。就像漏水、裂缝是房屋病灶,而“门窗”“楼梯”只是构件。

Q2. 重构的核心目标是什么?

单选题 · easy

  • A.提升代码可读性与可维护性← 正确答案
  • B.替换运行时底层编程语言
  • C.压缩二进制产物体积
  • D.消除产品需求歧义

答案:A。重构在保持外部行为不变的前提下改善内部结构,目标是可读性和可维护性。替换语言属于重写,压缩产物属于构建优化,消除需求歧义属于需求分析,都不是重构的范畴。

Q3. 提取函数最适合处理哪种坏味道?

单选题 · mid

  • A.长度过大的单个方法← 正确答案
  • B.全局变量数量过多
  • C.外部依赖版本冲突
  • D.配置文件格式混乱

答案:A。提取函数的动机就是“方法太长、一段逻辑可以被命名并独立出去”,它是长方法的对症药。全局变量多属于组织数据类问题,依赖冲突是构建工具问题,配置混乱是工程管理问题,都和不适用提取函数。

Q4. 技术债务的本质是什么?

单选题 · mid

  • A.为短期速度牺牲长期可维护性← 正确答案
  • B.为代码极简而进行的过度设计
  • C.因测试缺失导致的频繁线上故障
  • D.因硬件老化导致的性能下降

答案:A。技术债务特指为了快速交付而主动或被动选择“将来要还”的代码捷径,短期赢了速度,长期要付利息。过度设计是反面问题,测试缺失和硬件老化是独立的质量与运维问题,都未触及技术债务的“借与还”本质。

Q5. 进行小步重构时,最合理的做法是什么?

单选题 · mid

  • A.每次改动后立即运行相关测试← 正确答案
  • B.集中一周完成全量代码改造
  • C.先重写代码再补充测试用例
  • D.编译通过即可跳过测试直接提交

答案:A。小步重构的核心就是“小步验证”:改动小、反馈快、每次测试确认行为不变。集中大规模改造违反小步原则;先重写再补测试把重构变成了重写;编译通过只保证语法正确,不保证行为正确。

Q6. 在圈复杂度公式 v(G) = e − n + 2p 中,p 表示什么?

单选题 · hard

  • A.连通分量数← 正确答案
  • B.判断节点总数
  • C.代码注释行数
  • D.被调用函数数

答案:A。v(G) = e − n + 2p 中,e 是边数、n 是节点数、p 是连通分量数,通常为 1。判断节点已计入 n,注释和调用关系与公式无关,因此只有 A 正确。

Q7. 重构过程中,回归验证的首要保障是什么?

单选题 · hard

  • A.覆盖核心逻辑的自动化单元测试← 正确答案
  • B.线上环境直接执行全量压测
  • C.依赖作者本人对代码的记忆
  • D.静态扫描工具保证语法无误

答案:A。单元测试在重构前固化“行为基线”,重构后对比结果,是结构改动安全性的第一道防线。压测验证性能不验证行为,作者记忆不可靠,静态扫描只查语法不查语义。

Q8. 关于测试金字塔,下列哪项表述是正确的?

单选题 · extreme

  • A.单元测试数量最多且执行速度最快← 正确答案
  • B.端到端测试数量最多且维护成本最低
  • C.集成测试数量最少且运行速度最快
  • D.金字塔各层测试数量应平均分配

答案:A。测试金字塔自上而下是端到端(最少、最慢、最贵)、集成(中等)、单元(最多、最快、最便宜)。B、C 描述与事实相反,D 违背了“金字塔”的比例原则。

Q9. 以下哪些属于常见的重构手法分类?

多选题 · mid

  • A.组合方法← 正确答案
  • B.移动特性← 正确答案
  • C.简化条件← 正确答案
  • D.随机优化

答案:ABC。组合方法(提取函数、内联函数)、移动特性(搬移函数、搬移字段)、简化条件(卫语句、多态替换条件)都是 Fowler 重构目录中的标准分类。随机优化不是系统化重构手法,更像无方向的试探。

Q10. 长方法坏味道常见的负面影响包括哪些?

多选题 · hard

  • A.方法内职责混杂难以复用← 正确答案
  • B.阅读时需要反复滚动定位逻辑← 正确答案
  • C.嵌套层级过深导致测试成本高← 正确答案
  • D.编译速度变慢且内存占用升高

答案:ABC。长方法的直接影响是职责混杂、可读性差(滚动阅读)、嵌套深导致圈复杂度高、测试困难,这些都是结构层面的问题。编译速度和内存占用与进程运行时资源相关,与代码结构好坏没有必然联系。