每个工程师都背过 DRY(Don't Repeat Yourself):不要重复自己,见到两段相似的代码,就该抽一个公共函数出来。这条铁律如此正确,以至于很少有人追问:抽象一定比重复便宜吗? 2014 年的 RailsConf 大会上,Ruby 社区的传奇讲师 Sandi Metz 当着几百位工程师的面,把这条圣条拆了。她的结论只有一句话:duplication is far cheaper than the wrong abstraction —— 重复远比错误的抽象便宜。 这场名为 All the Little Things 的演讲,十多年过去,依然是程序员社区被引用最多的重构课之一。它讲的不是新框架、不是新语言,而是一个每个写代码的人都逃不掉的抉择:面对一坨祖传代码和一张新需求单,你到底该抄一遍,还是该抽一层。更难得的是,她不是在讲道理,而是现场把一段臭名昭著的烂代码,一刀一刀重构成能优雅接住新需求的形状——全程不到四十分钟,刀刀见血。如果你只想看一场“怎么做重构”的演示,它是教科书;如果你还想听“为什么绝大多数抽象都抽错了”,它几乎是孤本。
| 项目 | 内容 |
|---|---|
| 视频标题 | All the Little Things |
| 演讲者 | Sandi Metz(《面向对象设计实践指南:Ruby 语言版》作者) |
| 频道 | Confreaks |
| 场合 | RailsConf 2014 |
| 时长 | 约40分钟 |
| 播放量 | 数十万级(Confreaks 官方频道,数据截至发文时) |
| 核心主题 | 现场重构金玫瑰难题,论证「重复远比错误的抽象便宜」 |
演讲的道具是社区里流传多年的 Gilded Rose(金玫瑰旅馆)难题:一家旅店要管理库存商品的“质量值”,规则琐碎得像现实本身——普通物品每天贬值 1 点,过期后翻倍贬值;Aged Brie 越放越升值;传奇物品 Sulfuras 永远不变;演出门票 Backstage 则是越临近演出越升值,临期 10 天内每天涨 2,5 天内每天涨 3,演出一结束直接归零。而所有物品的质量都卡在 0 到 50 之间。负责这一切的,是一个写满 if 判断的巨型方法,夹杂着 s、t 这种天书般的变量名。现在新需求来了:新增“施法物品”(Conjured),变质速度是普通物品的两倍——而且老板不许你大改老代码的对外行为。这就是每个工程师都做过的噩梦:需求很小,代码很烂,责任很大。(视频设定部分为 kata 的经典背景,属 Sandi Metz 引用的社区难题)
整场演讲真正的靶子,是大家对 DRY 的教条化执行。Sandi Metz 的做法是把“重复”重新定价:重复代码的代价是线性的、局部的——抄了两遍,将来规则变了就改两处,改漏了好歹看得见;而错误抽象的代价是复利的、全局的——一个抽错的公共函数,会随着每个新需求长出新的参数开关,每个调用点都要理解全部分支,最后没人敢动它。她在演讲中反复用一个词:cost(代价)。判断抄还是抽,不看代码像不像,看代价记在哪一栏。我的延伸分析是:DRY 这条原则出自《程序员修炼之道》(The Pragmatic Programmer),本意是“每一条知识在系统里应该只有唯一的权威表述”——针对的是知识,不是字面上的相似。后人把它执行成“见重复就抽函数”,把手段供成了目的。Sandi Metz 做的,其实是把这条戒律从道德法庭拉回了成本会计。
面对那个巨型 if 方法,她的第一反应出人意料:不设计。不画类图,不谈架构,而是做最机械的事——把层层嵌套的条件展平,把大方法切成一个个小方法,每切一小步就跑一遍测试确认行为没变。她在演讲中展示的重构速度非常快,因为她已经做过很多遍,但每一个动作都小到可以随时回退。这一段传达的手感极其重要:重构不是一次大手术,而是一串小步快走。延伸一层:Kent Beck 有句被反复引用的话——"make the change easy, then make the easy change"(先把改动变容易,再做那个容易的改动)。很多团队的问题恰恰是想跳过第一步直接“做改动”,结果在烂代码上叠加更多烂代码。Sandi Metz 用四十分钟证明了:在测试兜底的前提下,“变容易”这个过程本身可以快到令人发指。
展平之后,她做的第二件事几乎不涉及任何“技术”:改名。把 s、t 这类魔法变量,一个一个换成业务词汇——不是程序员的语言,而是旅店店员的语言。名字改对之后,一段原本需要读三遍才能猜出意图的代码,变得像一份需求文档。这一段常被观众低估,但在我看来它是全场最实用的一节:命名是成本最低、收益最高的重构,因为它是在给未来的维护者(很可能就是你自己)写信。当一个方法必须靠注释才能解释清楚,而一个名字就能让注释下岗时,省钱的地方是下一次阅读——而代码被读的次数,永远比被写的次数多一个数量级。名字起对了,后面的类该怎么拆,往往会自己浮出水面;名字起错了,再漂亮的架构也是海市蜃楼。
真正的结构手术发生在这一步:用多态替换条件分支(Replace Conditional with Polymorphism,这是 Martin Fowler《重构》目录里的经典手法)。她把那个巨无霸方法按物品类型拆开——普通物品、Aged Brie、Backstage、Sulfuras 各自成为一个几行的小类,各自只负责自己的更新规则。那个曾经让所有人恐惧的 if 长梯消失了,因为它回答的问题(“这是什么类型的物品”)被语言本身的分派机制接管了。这是 Sandi Metz 的招牌主张:小方法、小类、小对象。她不追求那种“一个类管全局”的优雅,恰恰相反,她相信许多个只会一件事的小零件,比一个什么都会的大零件便宜得多。我的延伸看法:这一步能成立,前提是前三步——没有展平和改名,你连“条件到底在分派什么”都看不清;抽象的机会,是清理出来的,不是想出来的。
重构完成后的验收环节,是整场演讲的高潮。回到那张需求单:新增 Conjured 物品,变质速度翻倍。在重构前的代码里,这意味着往巨型 if 里再塞一根枝条,并且祈祷不弄坏其他四种物品。而现在,答案是一个几行的小类,复制普通物品的模板、把贬值速度改成 2——主流程一行都不用动。这一刻把全场的论点钉死了:可维护性不是一种美学偏好,而是可以精确度量的东西——接入一个新需求需要改动多少行既有代码。好的设计里,复用是重构的副产品,自动长出来;坏的设计里,复用是预先设计的目标,结果抽错了方向,每一个复用者都变成了人质。Sandi Metz 用一个 kata 说清了软件设计里最难的一课:面向未来设计的最好方式,是先把今天的代码弄干净,而不是猜明天的需求。
演讲结尾,她给出了那句被整个行业引用至今的判词。错误的抽象长什么样? 它通常始于一次善意:有人看到两段相似代码,好心抽成了公共函数;第三段相似但不完全相同的代码来了,为了塞进同一个函数,加了一个布尔参数;第四段来了,参数变成枚举;最后,这个函数里挤满了 if (flag) 开关,每个调用点都必须理解全部分支才能放心调用——重复代码只需要读一处,错误抽象却要求你读全部。她后来在一篇广为流传的博客 The Wrong Abstraction 里,把这套主张压缩成一句口号:AHA——Avoid Hasty Abstractions,别急着抽象。宁可以先抄三遍,等规则的形状自己稳定下来再动手。这不是反对抽象,而是反对用想象中的未来绑架真实的现在。这句话值得贴在每一间办公室的墙上。
flowchart TD
A["发现重复代码"] --> B{"规则还会变吗"}
B -->|还在抖| C["先抄第三遍"]
B -->|已稳定| D["开始抽象"]
C --> D
D --> E["展平嵌套条件"]
E --> F["切成小方法"]
F --> G["换成业务语言命名"]
G --> H{"分支逻辑属于谁"}
H -->|按业务类型| I["拆小类用多态"]
H -->|只差几个数字| J["保留参数化函数"]
I --> K["新需求几行接入"]
J --> K
K --> L["可维护性可度量"]
把这场演讲带回到你自己的工位上,至少有三件事可以立刻做。
第一,抽象之前过三问。 这条规则稳定了吗?我是不是已经抄到第三遍了?抽象之后,参数会不会长出“开关”?三问里只要有一问答不上来,就先抄,别抽。社区里对应的经验法则是“三次法则”(Rule of Three):第三次出现重复时再抽象,前两次都忍住。这不是懒,这是让真实需求替你完成设计调研。
第二,改一改代码评审的话术。 下次评审看到同事抽了个带布尔参数的公共函数,别只说“有点复杂”,可以直接问:“这个开关的两条分支,各自的调用方在哪里?”如果答案是“以后可能会有”,那就是在给未来放高利贷。反过来,看到有人老老实实抄了两遍几乎一样的代码,也别急着打回——先问规则会不会变,而不是数重复了几行。
第三,用它审 AI 生成的代码。 这是我认为这条十一年前的原则在今天反而更值钱的原因:AI 生成重复代码几乎零成本,生成错误抽象也几乎零成本——而且它特别擅长“看起来很专业”的过早抽象,抽出一堆没人需要的接口和参数。人作为 reviewer 的核心价值,恰恰是 Sandi Metz 这套账本思维:判断哪一笔债值得背,哪一笔是伪装成资产的负债。工具越快,判断越贵。
一句话:重构的本质不是让代码变漂亮,而是让下一次改动变便宜。
这场演讲用四十分钟证明了一件事:代码的债不是重复,而是错误的抽象——宁可老老实实抄三遍,也不要凭想象抽一层。
本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
