代码重复比错误的抽象要便宜得多 (2016)

2026-06-21 1 阅读 rafaepta
错误的抽象 我最初为我的 Chainline Newsletter 写了以下内容,但我不断收到有关此想法的推文,因此我在我的博客上重新发布了这篇文章。此版本经过轻微编辑。我一直在思考“错误抽象”的后果。我在 RailsConf 2014 的“所有小事”演讲中有一个部分,我断言:重复比错误的抽象要便宜得多。在总结中,我继续建议:更喜欢重复,而不是错误的抽象。一个更大的演讲中的这一小部分引起了令人惊讶的强烈反应。有些人认为我疯了,但更多人表达了这样的情绪:这,一百万倍! ” @BonzoESC :“重复比错误的抽象要便宜得多” @sandimetz @rbonales pic.twitter.com/3qMI0waqWb “ — 41 种蓝色 (@pims) 2014 年 3 月 7 日强烈的反应让我意识到“错误的抽象”问题是多么普遍和棘手。我开始提出问题,并发现以下模式:程序员 A 看到重复。程序员 A 提取重复项并为其命名。这创建了一个新的抽象。它可能是一种新方法,甚至可能是一个新类。程序员 A 用新的抽象替换了重复的内容。嗯,代码很完美。程序员A高兴地小跑着走了。时间流逝。出现了一个新的需求,当前的抽象几乎是完美的。程序员 B 的任务是实现此要求。程序员 B 觉得有义务保留现有的抽象,但由于每种情况并不完全相同,因此他们更改代码以获取参数,然后添加逻辑以根据该参数的值有条件地执行正确的操作。曾经的通用抽象现在在不同情况下表现不同。又一个新的要求到来了。程序员 X。另一个附加参数。另一个新的条件。循环直到代码变得难以理解。你出现在关于这里的故事中,你的生活发生了戏剧性的转变,变得更糟。现有代码具有强大的影响力。它的存在本身就表明它既正确又必要。我们知道代码代表了所付出的努力,并且我们非常有动力去保护这种努力的价值。不幸的是,可悲的事实是,代码越复杂和难以理解,即创建它的投资越深,我们就越感到保留它的压力(“沉没成本谬误”)。就好像我们的潜意识告诉我们“天哪,这太令人困惑了,一定花了很长时间才弄好。当然,这真的非常非常重要。如果让所有的努力白费,那将是一种罪恶。”当你出现在上面第8步的这个故事中时,这种压力可能会迫使你继续前进,即通过更改现有代码来实现新的需求。然而,尝试这样做是残酷的。代码不再代表一个单一的、常见的抽象,而是成为一个充满条件的过程,其中交织着许多模糊关联的想法。它很难理解并且很容易被破坏。如果您发现自己处于这种情况,请不要被沉没成本所驱使。当处理错误的抽象时,最快的前进方式是返回。执行以下操作: 通过将抽象代码内联回每个调用者来重新引入重复。在每个调用方中,使用传递的参数来确定该特定调用方执行的内联代码的子集。删除该特定调用者不需要的位。这消除了抽象和条件,并将每个调用者减少到仅需要它的代码。当您以这种方式回顾决策时,通常会发现尽管每个调用者表面上都调用了共享抽象,但他们运行的代码却相当独特。一旦完全删除旧的抽象,您就可以重新开始,重新隔离重复并重新提取抽象。我见过一些问题,人们勇敢地尝试以错误的抽象方式前进,但收效甚微。添加新功能非常困难,每一次成功都会使代码变得更加复杂,这使得添加下一个功能变得更加困难。当他们将观点从“我必须保留对这段代码的投资”转变为“这段代码在一段时间内是有意义的,但也许我们已经从中学到了我们能学到的一切”,并允许自己根据当前的需求重新思考他们的抽象时,一切都变得更容易了。一旦他们内嵌了代码,前进的道路就变得显而易见,添加新功能变得更快更容易。这个故事的寓意是什么?不要陷入沉没成本谬论。如果您发现自己通过共享代码传递参数并添加条件路径,则抽象是不正确的。一开始也许是对的,但那一天已经过去了。一旦抽象被证明是错误的,最好的策略是重新引入重复并让它向您展示w